Kea2-python 1.1.0b1__py3-none-any.whl
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.
- kea2/__init__.py +8 -0
- kea2/absDriver.py +56 -0
- kea2/adbUtils.py +554 -0
- kea2/assets/config_version.json +16 -0
- kea2/assets/fastbot-thirdpart.jar +0 -0
- kea2/assets/fastbot_configs/abl.strings +2 -0
- kea2/assets/fastbot_configs/awl.strings +3 -0
- kea2/assets/fastbot_configs/max.config +7 -0
- kea2/assets/fastbot_configs/max.fuzzing.strings +699 -0
- kea2/assets/fastbot_configs/max.schema.strings +1 -0
- kea2/assets/fastbot_configs/max.strings +3 -0
- kea2/assets/fastbot_configs/max.tree.pruning +27 -0
- kea2/assets/fastbot_configs/teardown.py +18 -0
- kea2/assets/fastbot_configs/widget.block.py +38 -0
- kea2/assets/fastbot_libs/arm64-v8a/libfastbot_native.so +0 -0
- kea2/assets/fastbot_libs/armeabi-v7a/libfastbot_native.so +0 -0
- kea2/assets/fastbot_libs/x86/libfastbot_native.so +0 -0
- kea2/assets/fastbot_libs/x86_64/libfastbot_native.so +0 -0
- kea2/assets/framework.jar +0 -0
- kea2/assets/kea2-thirdpart.jar +0 -0
- kea2/assets/monkeyq.jar +0 -0
- kea2/assets/quicktest.py +126 -0
- kea2/cli.py +216 -0
- kea2/fastbotManager.py +269 -0
- kea2/kea2_api.py +166 -0
- kea2/keaUtils.py +926 -0
- kea2/kea_launcher.py +299 -0
- kea2/logWatcher.py +92 -0
- kea2/mixin.py +0 -0
- kea2/report/__init__.py +0 -0
- kea2/report/bug_report_generator.py +879 -0
- kea2/report/mixin.py +496 -0
- kea2/report/report_merger.py +1066 -0
- kea2/report/templates/bug_report_template.html +4028 -0
- kea2/report/templates/merged_bug_report_template.html +3602 -0
- kea2/report/utils.py +10 -0
- kea2/result.py +257 -0
- kea2/resultSyncer.py +65 -0
- kea2/state.py +22 -0
- kea2/typedefs.py +32 -0
- kea2/u2Driver.py +612 -0
- kea2/utils.py +192 -0
- kea2/version_manager.py +102 -0
- kea2_python-1.1.0b1.dist-info/METADATA +447 -0
- kea2_python-1.1.0b1.dist-info/RECORD +49 -0
- kea2_python-1.1.0b1.dist-info/WHEEL +5 -0
- kea2_python-1.1.0b1.dist-info/entry_points.txt +2 -0
- kea2_python-1.1.0b1.dist-info/licenses/LICENSE +16 -0
- kea2_python-1.1.0b1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,4028 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Kea2 Test Report</title>
|
|
7
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
8
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
|
|
9
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
10
|
+
<style>
|
|
11
|
+
:root {
|
|
12
|
+
--primary-color: #3498db;
|
|
13
|
+
--secondary-color: #2ecc71;
|
|
14
|
+
--warning-color: #f39c12;
|
|
15
|
+
--danger-color: #e74c3c;
|
|
16
|
+
--dark-color: #2c3e50;
|
|
17
|
+
--light-color: #ecf0f1;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
body {
|
|
21
|
+
font-family: 'Segoe UI', Roboto, -apple-system, sans-serif;
|
|
22
|
+
background-color: #f8f9fa;
|
|
23
|
+
color: #333;
|
|
24
|
+
line-height: 1.6;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* Custom container width - wider than Bootstrap default */
|
|
28
|
+
.container {
|
|
29
|
+
max-width: 98% !important;
|
|
30
|
+
width: 98% !important;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@media (min-width: 1200px) {
|
|
34
|
+
.container {
|
|
35
|
+
max-width: 1800px !important;
|
|
36
|
+
width: 95% !important;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@media (min-width: 1400px) {
|
|
41
|
+
.container {
|
|
42
|
+
max-width: 2000px !important;
|
|
43
|
+
width: 92% !important;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@media (min-width: 1600px) {
|
|
48
|
+
.container {
|
|
49
|
+
max-width: 2200px !important;
|
|
50
|
+
width: 90% !important;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@media (min-width: 1800px) {
|
|
55
|
+
.container {
|
|
56
|
+
max-width: 2400px !important;
|
|
57
|
+
width: 88% !important;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@media (min-width: 2000px) {
|
|
62
|
+
.container {
|
|
63
|
+
max-width: 2600px !important;
|
|
64
|
+
width: 85% !important;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.header {
|
|
69
|
+
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
|
70
|
+
color: white;
|
|
71
|
+
padding: 2.5rem 0;
|
|
72
|
+
margin-bottom: 3rem;
|
|
73
|
+
border-radius: 0 0 20px 20px;
|
|
74
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.stats-card {
|
|
78
|
+
border-radius: 12px;
|
|
79
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
|
|
80
|
+
transition: transform 0.3s, box-shadow 0.3s;
|
|
81
|
+
height: 100%;
|
|
82
|
+
overflow: hidden;
|
|
83
|
+
padding: 1rem;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.stats-card:hover {
|
|
87
|
+
transform: translateY(-5px);
|
|
88
|
+
box-shadow: 0 8px 15px rgba(0, 0, 0, 0.1);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.card-header {
|
|
92
|
+
font-weight: 600;
|
|
93
|
+
padding: 1.25rem 1.5rem;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.card-body {
|
|
97
|
+
padding: 1.5rem;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.screenshots-container {
|
|
101
|
+
display: flex;
|
|
102
|
+
overflow-x: auto;
|
|
103
|
+
gap: 15px;
|
|
104
|
+
padding: 15px 0;
|
|
105
|
+
scrollbar-width: thin;
|
|
106
|
+
scrollbar-color: var(--primary-color) #eee;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.screenshots-container::-webkit-scrollbar {
|
|
110
|
+
height: 8px;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.screenshots-container::-webkit-scrollbar-track {
|
|
114
|
+
background: #eee;
|
|
115
|
+
border-radius: 10px;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.screenshots-container::-webkit-scrollbar-thumb {
|
|
119
|
+
background-color: var(--primary-color);
|
|
120
|
+
border-radius: 10px;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.screenshot-item {
|
|
124
|
+
flex: 0 0 auto;
|
|
125
|
+
width: 300px;
|
|
126
|
+
position: relative;
|
|
127
|
+
transition: transform 0.2s;
|
|
128
|
+
margin-bottom: 10px;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.screenshot-item:hover {
|
|
132
|
+
transform: scale(1.03);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.screenshot-img {
|
|
136
|
+
width: 300px;
|
|
137
|
+
height: 400px;
|
|
138
|
+
object-fit: contain;
|
|
139
|
+
border-radius: 8px;
|
|
140
|
+
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
|
|
141
|
+
margin-bottom: 8px;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.screenshot-placeholder {
|
|
145
|
+
width: 300px;
|
|
146
|
+
height: 400px;
|
|
147
|
+
border-radius: 8px;
|
|
148
|
+
background: linear-gradient(180deg, rgba(52, 152, 219, 0.08), rgba(255, 255, 255, 0.98));
|
|
149
|
+
border: 1px solid rgba(52, 152, 219, 0.18);
|
|
150
|
+
box-shadow: 0 8px 24px rgba(44, 62, 80, 0.08);
|
|
151
|
+
display: flex;
|
|
152
|
+
align-items: center;
|
|
153
|
+
justify-content: center;
|
|
154
|
+
margin-bottom: 8px;
|
|
155
|
+
color: #6c757d;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.screenshot-placeholder::before {
|
|
159
|
+
content: "";
|
|
160
|
+
position: absolute;
|
|
161
|
+
inset: 10px;
|
|
162
|
+
border-radius: 10px;
|
|
163
|
+
border: 1px dashed rgba(52, 152, 219, 0.25);
|
|
164
|
+
pointer-events: none;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.screenshot-item .screenshot-placeholder {
|
|
168
|
+
position: relative;
|
|
169
|
+
overflow: hidden;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.screenshot-placeholder::after {
|
|
173
|
+
content: "";
|
|
174
|
+
position: absolute;
|
|
175
|
+
inset: 0;
|
|
176
|
+
background:
|
|
177
|
+
radial-gradient(circle at 20% 25%, rgba(46, 204, 113, 0.18), transparent 42%),
|
|
178
|
+
radial-gradient(circle at 80% 70%, rgba(52, 152, 219, 0.16), transparent 48%);
|
|
179
|
+
pointer-events: none;
|
|
180
|
+
opacity: 0.9;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.screenshot-placeholder .placeholder-content {
|
|
184
|
+
position: relative;
|
|
185
|
+
z-index: 1;
|
|
186
|
+
display: flex;
|
|
187
|
+
flex-direction: column;
|
|
188
|
+
align-items: center;
|
|
189
|
+
gap: 12px;
|
|
190
|
+
text-align: center;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.screenshot-placeholder .placeholder-icon {
|
|
194
|
+
width: 92px;
|
|
195
|
+
height: 92px;
|
|
196
|
+
border-radius: 999px;
|
|
197
|
+
display: inline-flex;
|
|
198
|
+
align-items: center;
|
|
199
|
+
justify-content: center;
|
|
200
|
+
box-shadow: 0 16px 32px rgba(52, 152, 219, 0.22);
|
|
201
|
+
border: 1px solid rgba(52, 152, 219, 0.22);
|
|
202
|
+
background: radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.9), rgba(52, 152, 219, 0.10));
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.screenshot-placeholder .placeholder-icon i {
|
|
206
|
+
font-size: 40px;
|
|
207
|
+
color: var(--primary-color);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.screenshot-placeholder .placeholder-icon.info i {
|
|
211
|
+
color: #6c757d;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.screenshot-placeholder .placeholder-icon.reboot {
|
|
215
|
+
background: conic-gradient(from 210deg, rgba(46, 204, 113, 0.22), rgba(52, 152, 219, 0.22), rgba(46, 204, 113, 0.22));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.screenshot-placeholder .placeholder-text {
|
|
219
|
+
font-size: 13px;
|
|
220
|
+
font-weight: 600;
|
|
221
|
+
letter-spacing: 0.2px;
|
|
222
|
+
color: rgba(44, 62, 80, 0.78);
|
|
223
|
+
padding: 6px 10px;
|
|
224
|
+
border-radius: 999px;
|
|
225
|
+
background: rgba(255, 255, 255, 0.72);
|
|
226
|
+
border: 1px solid rgba(52, 152, 219, 0.14);
|
|
227
|
+
box-shadow: 0 6px 18px rgba(44, 62, 80, 0.06);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.screenshot-item:hover .screenshot-placeholder {
|
|
231
|
+
border-color: rgba(52, 152, 219, 0.28);
|
|
232
|
+
box-shadow: 0 12px 30px rgba(44, 62, 80, 0.10);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.screenshot-item:hover .placeholder-icon.reboot i {
|
|
236
|
+
animation: reboot-spin 1.4s ease-in-out infinite;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
@keyframes reboot-spin {
|
|
240
|
+
0% { transform: rotate(0deg); }
|
|
241
|
+
60% { transform: rotate(220deg); }
|
|
242
|
+
100% { transform: rotate(360deg); }
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.screenshot-caption {
|
|
246
|
+
font-size: 13px;
|
|
247
|
+
color: #555;
|
|
248
|
+
padding: 12px 8px;
|
|
249
|
+
font-weight: 500;
|
|
250
|
+
text-align: center;
|
|
251
|
+
background-color: white;
|
|
252
|
+
border-radius: 8px;
|
|
253
|
+
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.05);
|
|
254
|
+
line-height: 1.5;
|
|
255
|
+
white-space: normal;
|
|
256
|
+
word-wrap: break-word;
|
|
257
|
+
overflow-wrap: anywhere;
|
|
258
|
+
min-height: 50px;
|
|
259
|
+
display: flex;
|
|
260
|
+
flex-direction: column;
|
|
261
|
+
justify-content: center;
|
|
262
|
+
align-items: center;
|
|
263
|
+
max-width: 100%;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.screenshot-caption .step-number {
|
|
267
|
+
display: block;
|
|
268
|
+
font-weight: 600;
|
|
269
|
+
color: var(--primary-color);
|
|
270
|
+
font-size: 12px;
|
|
271
|
+
margin-bottom: 4px;
|
|
272
|
+
max-width: 100%;
|
|
273
|
+
word-break: break-word;
|
|
274
|
+
overflow-wrap: anywhere;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.screenshot-caption .step-action {
|
|
278
|
+
display: block;
|
|
279
|
+
font-size: 13px;
|
|
280
|
+
color: #666;
|
|
281
|
+
font-weight: 400;
|
|
282
|
+
line-height: 1.3;
|
|
283
|
+
max-width: 100%;
|
|
284
|
+
word-break: break-word;
|
|
285
|
+
overflow-wrap: anywhere;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.table-custom {
|
|
289
|
+
border-radius: 10px;
|
|
290
|
+
overflow: hidden;
|
|
291
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
|
|
292
|
+
width: 100%;
|
|
293
|
+
table-layout: auto;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.crash-analysis-panel {
|
|
297
|
+
background-color: transparent;
|
|
298
|
+
border-radius: 0;
|
|
299
|
+
padding: 0;
|
|
300
|
+
box-shadow: none;
|
|
301
|
+
border: none;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.crash-analysis-panel .btn-group .btn {
|
|
305
|
+
font-weight: 600;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.table-custom thead {
|
|
309
|
+
background-color: #495057;
|
|
310
|
+
color: white;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.table-custom th {
|
|
314
|
+
font-weight: 600;
|
|
315
|
+
padding: 15px 12px;
|
|
316
|
+
white-space: nowrap;
|
|
317
|
+
text-align: center;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.table-custom td {
|
|
321
|
+
padding: 15px 12px;
|
|
322
|
+
vertical-align: middle;
|
|
323
|
+
text-align: center;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/* Override text alignment for stack trace containers */
|
|
327
|
+
.table-custom td .bg-light {
|
|
328
|
+
text-align: left;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
.table-custom td .bg-light pre {
|
|
332
|
+
text-align: left !important;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/* Enhanced Error Details styling for Property Statistics */
|
|
336
|
+
.table-custom tr.collapse {
|
|
337
|
+
position: relative;
|
|
338
|
+
z-index: 10;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.table-custom tr.collapse td {
|
|
342
|
+
border-top: none;
|
|
343
|
+
padding-top: 0;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.table-custom tr.collapse .bg-light {
|
|
347
|
+
max-width: none;
|
|
348
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
349
|
+
border: 1px solid #e9ecef;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.table-custom tr.collapse pre {
|
|
353
|
+
font-size: 0.85rem;
|
|
354
|
+
line-height: 1.4;
|
|
355
|
+
background-color: #f8f9fa;
|
|
356
|
+
border: 1px solid #e9ecef;
|
|
357
|
+
border-radius: 4px;
|
|
358
|
+
padding: 0.75rem;
|
|
359
|
+
white-space: pre-wrap;
|
|
360
|
+
word-wrap: break-word;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.table-custom tr.collapse details {
|
|
364
|
+
margin-top: 0.5rem;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.table-custom tr.collapse summary {
|
|
368
|
+
cursor: pointer;
|
|
369
|
+
margin-bottom: 0.5rem;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/* Specific column widths for property statistics table */
|
|
373
|
+
.table-custom th:nth-child(1), .table-custom td:nth-child(1) { /* Index */
|
|
374
|
+
width: 5%;
|
|
375
|
+
min-width: 50px;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.table-custom th:nth-child(2), .table-custom td:nth-child(2) { /* Property Name */
|
|
379
|
+
width: 18%;
|
|
380
|
+
min-width: 180px;
|
|
381
|
+
text-align: left;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.table-custom th:nth-child(3), .table-custom td:nth-child(3) { /* Precondition Satisfied */
|
|
385
|
+
width: 9%;
|
|
386
|
+
min-width: 85px;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.table-custom th:nth-child(4), .table-custom td:nth-child(4) { /* Executed */
|
|
390
|
+
width: 8%;
|
|
391
|
+
min-width: 70px;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.table-custom th:nth-child(5), .table-custom td:nth-child(5) { /* Fails */
|
|
395
|
+
width: 7%;
|
|
396
|
+
min-width: 65px;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
.table-custom th:nth-child(6), .table-custom td:nth-child(6) { /* Errors */
|
|
400
|
+
width: 8%;
|
|
401
|
+
min-width: 70px;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.table-custom th:nth-child(7), .table-custom td:nth-child(7) { /* Error Details */
|
|
405
|
+
width: 45%;
|
|
406
|
+
min-width: 400px;
|
|
407
|
+
text-align: center;
|
|
408
|
+
position: relative;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/* Property Checking Statistics specific adjustments */
|
|
412
|
+
.table-property-stats th,
|
|
413
|
+
.table-property-stats td {
|
|
414
|
+
padding: 20px 14px;
|
|
415
|
+
line-height: 1.65;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.table-property-stats th:nth-child(1),
|
|
419
|
+
.table-property-stats td:nth-child(1) {
|
|
420
|
+
width: 6%;
|
|
421
|
+
min-width: 60px;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.table-property-stats th:nth-child(2),
|
|
425
|
+
.table-property-stats td:nth-child(2) {
|
|
426
|
+
width: 22%;
|
|
427
|
+
min-width: 200px;
|
|
428
|
+
text-align: left;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.table-property-stats th:nth-child(3),
|
|
432
|
+
.table-property-stats td:nth-child(3) {
|
|
433
|
+
width: 10%;
|
|
434
|
+
min-width: 90px;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.table-property-stats th:nth-child(4),
|
|
438
|
+
.table-property-stats td:nth-child(4) {
|
|
439
|
+
width: 9%;
|
|
440
|
+
min-width: 80px;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.table-property-stats th:nth-child(5),
|
|
444
|
+
.table-property-stats td:nth-child(5) {
|
|
445
|
+
width: 9%;
|
|
446
|
+
min-width: 80px;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.table-property-stats th:nth-child(6),
|
|
450
|
+
.table-property-stats td:nth-child(6) {
|
|
451
|
+
width: 9%;
|
|
452
|
+
min-width: 80px;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.table-property-stats th:nth-child(7),
|
|
456
|
+
.table-property-stats td:nth-child(7) {
|
|
457
|
+
width: 9%;
|
|
458
|
+
min-width: 80px;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.table-property-stats th:nth-child(8),
|
|
462
|
+
.table-property-stats td:nth-child(8) {
|
|
463
|
+
width: 26%;
|
|
464
|
+
min-width: 260px;
|
|
465
|
+
text-align: left;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.table-property-stats td:nth-child(8) {
|
|
469
|
+
display: table-cell;
|
|
470
|
+
vertical-align: middle;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
.table-custom tbody tr:nth-of-type(odd) {
|
|
474
|
+
background-color: rgba(0, 0, 0, 0.02);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
.table-custom tbody tr:hover {
|
|
478
|
+
background-color: rgba(0, 0, 0, 0.05);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
.stat-value {
|
|
482
|
+
font-size: 2.2rem;
|
|
483
|
+
font-weight: 700;
|
|
484
|
+
display: block;
|
|
485
|
+
margin-bottom: 0.8rem;
|
|
486
|
+
line-height: 1.2;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.stat-label {
|
|
490
|
+
font-size: 1rem;
|
|
491
|
+
color: #666;
|
|
492
|
+
display: block;
|
|
493
|
+
margin-top: 5px;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.summary-grid {
|
|
497
|
+
display: grid;
|
|
498
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
499
|
+
gap: 24px;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
@media (max-width: 1200px) {
|
|
503
|
+
.summary-grid {
|
|
504
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
@media (max-width: 768px) {
|
|
509
|
+
.summary-grid {
|
|
510
|
+
grid-template-columns: 1fr;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.summary-item {
|
|
515
|
+
text-align: center;
|
|
516
|
+
padding: 20px 16px;
|
|
517
|
+
border-radius: 12px;
|
|
518
|
+
background-color: #f8fafc;
|
|
519
|
+
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
.summary-item:hover {
|
|
523
|
+
transform: translateY(-4px);
|
|
524
|
+
box-shadow: 0 6px 12px rgba(15, 23, 42, 0.08);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
.summary-item i {
|
|
528
|
+
font-size: 2rem;
|
|
529
|
+
display: block;
|
|
530
|
+
margin-bottom: 12px;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.chart-container {
|
|
534
|
+
background-color: white;
|
|
535
|
+
border-radius: 12px;
|
|
536
|
+
padding: 30px;
|
|
537
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
|
|
538
|
+
margin-bottom: 40px;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
.section-title {
|
|
542
|
+
position: relative;
|
|
543
|
+
padding-bottom: 15px;
|
|
544
|
+
margin-bottom: 30px;
|
|
545
|
+
font-weight: 600;
|
|
546
|
+
color: var(--dark-color);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
.section-title::after {
|
|
550
|
+
content: '';
|
|
551
|
+
position: absolute;
|
|
552
|
+
bottom: 0;
|
|
553
|
+
left: 0;
|
|
554
|
+
height: 3px;
|
|
555
|
+
width: 50px;
|
|
556
|
+
background: linear-gradient(to right, var(--primary-color), var(--secondary-color));
|
|
557
|
+
border-radius: 3px;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
.section-block {
|
|
561
|
+
margin-bottom: 70px;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
.value-highlight {
|
|
565
|
+
color: var(--primary-color);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
.value-danger {
|
|
569
|
+
color: var(--danger-color);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
.value-warning {
|
|
573
|
+
color: var(--warning-color);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
.value-success {
|
|
577
|
+
color: var(--secondary-color);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
.summary-card {
|
|
581
|
+
border-radius: 12px;
|
|
582
|
+
background-color: white;
|
|
583
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
|
|
584
|
+
padding: 30px;
|
|
585
|
+
margin-bottom: 40px;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
.link-button {
|
|
589
|
+
color: var(--primary-color);
|
|
590
|
+
text-decoration: none;
|
|
591
|
+
font-weight: 500;
|
|
592
|
+
transition: color 0.2s;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
.link-button:hover {
|
|
596
|
+
color: var(--secondary-color);
|
|
597
|
+
text-decoration: underline;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
.badge-custom {
|
|
601
|
+
padding: 6px 12px;
|
|
602
|
+
border-radius: 50px;
|
|
603
|
+
font-weight: 500;
|
|
604
|
+
font-size: 0.9rem;
|
|
605
|
+
margin: 0 2px;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
.activity-list {
|
|
609
|
+
height: 550px;
|
|
610
|
+
overflow-y: auto;
|
|
611
|
+
border-radius: 8px;
|
|
612
|
+
border: 1px solid rgba(0,0,0,0.1);
|
|
613
|
+
padding: 15px;
|
|
614
|
+
background-color: rgba(255,255,255,0.5);
|
|
615
|
+
scrollbar-width: thin;
|
|
616
|
+
scrollbar-color: var(--primary-color) #eee;
|
|
617
|
+
margin-bottom: 15px;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
.activity-list::-webkit-scrollbar {
|
|
621
|
+
width: 8px;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
.activity-list::-webkit-scrollbar-track {
|
|
625
|
+
background: #eee;
|
|
626
|
+
border-radius: 10px;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
.activity-list::-webkit-scrollbar-thumb {
|
|
630
|
+
background-color: var(--primary-color);
|
|
631
|
+
border-radius: 10px;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
.activities-container {
|
|
635
|
+
display: flex;
|
|
636
|
+
flex-direction: column;
|
|
637
|
+
height: 650px;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
.pagination-container {
|
|
641
|
+
padding: 10px 0;
|
|
642
|
+
background-color: white;
|
|
643
|
+
border-radius: 0 0 8px 8px;
|
|
644
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
.pagination-container.pagination-violations {
|
|
648
|
+
padding: 10px 16px;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
.activity-item {
|
|
652
|
+
padding: 10px 15px;
|
|
653
|
+
border-radius: 6px;
|
|
654
|
+
margin-bottom: 10px;
|
|
655
|
+
font-size: 0.95rem;
|
|
656
|
+
word-break: break-all;
|
|
657
|
+
background-color: white;
|
|
658
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
|
659
|
+
transition: all 0.2s;
|
|
660
|
+
line-height: 1.5;
|
|
661
|
+
display: flex;
|
|
662
|
+
align-items: center;
|
|
663
|
+
justify-content: space-between;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
.activity-item .activity-content {
|
|
667
|
+
display: flex;
|
|
668
|
+
align-items: center;
|
|
669
|
+
flex: 1;
|
|
670
|
+
min-width: 0;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
.activity-item .activity-name {
|
|
674
|
+
flex: 1;
|
|
675
|
+
word-break: break-all;
|
|
676
|
+
margin-right: 10px;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
.activity-item .traversal-badge {
|
|
680
|
+
flex-shrink: 0;
|
|
681
|
+
font-size: 0.85rem;
|
|
682
|
+
font-weight: 500;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
.activity-item:hover {
|
|
686
|
+
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
|
687
|
+
transform: translateX(3px);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
.nav-tabs .nav-link {
|
|
691
|
+
color: #666;
|
|
692
|
+
border: 1px solid transparent;
|
|
693
|
+
border-radius: 6px 6px 0 0;
|
|
694
|
+
font-weight: 500;
|
|
695
|
+
transition: all 0.3s ease;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
.nav-tabs .nav-link:hover {
|
|
699
|
+
color: var(--primary-color);
|
|
700
|
+
border-color: rgba(52, 152, 219, 0.2);
|
|
701
|
+
background-color: rgba(52, 152, 219, 0.05);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
.nav-tabs .nav-link.active {
|
|
705
|
+
color: var(--primary-color);
|
|
706
|
+
background-color: white;
|
|
707
|
+
border-color: #dee2e6 #dee2e6 #fff;
|
|
708
|
+
font-weight: 600;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
.tab-content {
|
|
712
|
+
border: 1px solid #dee2e6;
|
|
713
|
+
border-top: none;
|
|
714
|
+
border-radius: 0 0 8px 8px;
|
|
715
|
+
padding: 20px;
|
|
716
|
+
background-color: #fafafa;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
.sorting-controls {
|
|
720
|
+
background-color: #f8f9fa;
|
|
721
|
+
border: 1px solid #e9ecef;
|
|
722
|
+
border-radius: 8px;
|
|
723
|
+
padding: 15px;
|
|
724
|
+
margin-bottom: 20px;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
.sorting-controls .form-select {
|
|
728
|
+
min-width: 140px;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
.sorting-controls .btn {
|
|
732
|
+
transition: all 0.3s ease;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
.sorting-controls .btn:hover {
|
|
736
|
+
transform: translateY(-1px);
|
|
737
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
.sort-icon {
|
|
741
|
+
margin-left: 8px;
|
|
742
|
+
transition: all 0.3s ease;
|
|
743
|
+
font-size: 1.4rem;
|
|
744
|
+
color: #ffffff !important;
|
|
745
|
+
opacity: 0.6;
|
|
746
|
+
text-shadow: 0 0 3px rgba(0,0,0,0.3);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
.sort-icon:hover {
|
|
750
|
+
opacity: 1;
|
|
751
|
+
transform: scale(1.2);
|
|
752
|
+
text-shadow: 0 0 5px rgba(0,0,0,0.5);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
.sort-icon.active {
|
|
756
|
+
opacity: 1;
|
|
757
|
+
font-weight: bold;
|
|
758
|
+
text-shadow: 0 0 5px rgba(0,0,0,0.5);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
.sort-icon.asc.active {
|
|
762
|
+
color: #40e0d0 !important;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
.sort-icon.desc.active {
|
|
766
|
+
color: #ff6b6b !important;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
.table-violations thead {
|
|
770
|
+
background-color: #dc3545 !important;
|
|
771
|
+
color: white;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
.table-violations .sort-icon {
|
|
775
|
+
color: #ffffff !important;
|
|
776
|
+
text-shadow: 0 0 3px rgba(0,0,0,0.4);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
.table-violations .sort-icon:hover {
|
|
780
|
+
opacity: 1;
|
|
781
|
+
transform: scale(1.2);
|
|
782
|
+
text-shadow: 0 0 5px rgba(0,0,0,0.6);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/* Specific column widths for property violations table */
|
|
786
|
+
.table-violations th:nth-child(1), .table-violations td:nth-child(1) { /* Index */
|
|
787
|
+
width: 10%;
|
|
788
|
+
min-width: 60px;
|
|
789
|
+
text-align: center;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
.table-violations th:nth-child(2), .table-violations td:nth-child(2) { /* Property Name */
|
|
793
|
+
width: 40%;
|
|
794
|
+
min-width: 200px;
|
|
795
|
+
text-align: left;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
.table-violations th:nth-child(3), .table-violations td:nth-child(3) { /* Interaction Scenario Pages */
|
|
799
|
+
width: 50%;
|
|
800
|
+
min-width: 150px;
|
|
801
|
+
text-align: center;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
@media (max-width: 768px) {
|
|
805
|
+
.container {
|
|
806
|
+
max-width: 98% !important;
|
|
807
|
+
width: 98% !important;
|
|
808
|
+
padding-left: 10px !important;
|
|
809
|
+
padding-right: 10px !important;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
.stat-value {
|
|
813
|
+
font-size: 1.5rem;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
.screenshot-item {
|
|
817
|
+
width: 280px;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
.screenshot-img {
|
|
821
|
+
width: 280px;
|
|
822
|
+
height: 400px;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
.table-custom {
|
|
826
|
+
font-size: 0.9rem;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
.table-custom th, .table-custom td {
|
|
830
|
+
padding: 10px 6px;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
.badge-custom {
|
|
834
|
+
font-size: 0.8rem;
|
|
835
|
+
padding: 4px 8px;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
@media (max-width: 576px) {
|
|
840
|
+
.container {
|
|
841
|
+
max-width: 100% !important;
|
|
842
|
+
width: 100% !important;
|
|
843
|
+
padding-left: 5px !important;
|
|
844
|
+
padding-right: 5px !important;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
.screenshot-item {
|
|
848
|
+
width: 260px;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
.screenshot-img {
|
|
852
|
+
width: 260px;
|
|
853
|
+
height: 380px;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
.table-custom {
|
|
857
|
+
font-size: 0.8rem;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
.table-custom th, .table-custom td {
|
|
861
|
+
padding: 8px 4px;
|
|
862
|
+
white-space: normal;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
/* Modern Sorting Controls Styling */
|
|
867
|
+
.sorting-controls-modern {
|
|
868
|
+
background: #ffffff;
|
|
869
|
+
border: 1px solid #e5e7eb;
|
|
870
|
+
border-radius: 16px;
|
|
871
|
+
padding: 20px 24px;
|
|
872
|
+
margin-bottom: 16px;
|
|
873
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
|
|
874
|
+
transition: all 0.2s ease;
|
|
875
|
+
position: relative;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
.sorting-controls-modern:hover {
|
|
879
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.07), 0 2px 4px rgba(0, 0, 0, 0.06);
|
|
880
|
+
border-color: #d1d5db;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
.sort-label-section {
|
|
884
|
+
display: flex;
|
|
885
|
+
align-items: center;
|
|
886
|
+
gap: 16px;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
.sort-icon-wrapper {
|
|
890
|
+
width: 48px;
|
|
891
|
+
height: 48px;
|
|
892
|
+
background: linear-gradient(135deg, #3498db, #2980b9);
|
|
893
|
+
border-radius: 12px;
|
|
894
|
+
display: flex;
|
|
895
|
+
align-items: center;
|
|
896
|
+
justify-content: center;
|
|
897
|
+
box-shadow: 0 2px 8px rgba(52, 152, 219, 0.3);
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
.sort-icon-wrapper i {
|
|
901
|
+
color: white;
|
|
902
|
+
font-size: 20px;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
.sort-text {
|
|
906
|
+
display: flex;
|
|
907
|
+
flex-direction: column;
|
|
908
|
+
gap: 4px;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
.sort-title {
|
|
912
|
+
font-size: 16px;
|
|
913
|
+
font-weight: 600;
|
|
914
|
+
color: #2c3e50;
|
|
915
|
+
line-height: 1.2;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
.sort-subtitle {
|
|
919
|
+
font-size: 13px;
|
|
920
|
+
color: #7f8c8d;
|
|
921
|
+
font-weight: 400;
|
|
922
|
+
line-height: 1.2;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
.sort-button-section {
|
|
926
|
+
display: flex;
|
|
927
|
+
align-items: center;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
.btn-sort-modern {
|
|
931
|
+
background: linear-gradient(135deg, #27ae60, #2ecc71);
|
|
932
|
+
border: none;
|
|
933
|
+
border-radius: 10px;
|
|
934
|
+
padding: 12px 20px;
|
|
935
|
+
color: white;
|
|
936
|
+
font-weight: 500;
|
|
937
|
+
font-size: 14px;
|
|
938
|
+
cursor: pointer;
|
|
939
|
+
transition: all 0.3s ease;
|
|
940
|
+
box-shadow: 0 2px 8px rgba(46, 204, 113, 0.3);
|
|
941
|
+
position: relative;
|
|
942
|
+
overflow: hidden;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
.btn-sort-modern::before {
|
|
946
|
+
content: '';
|
|
947
|
+
position: absolute;
|
|
948
|
+
top: 0;
|
|
949
|
+
left: -100%;
|
|
950
|
+
width: 100%;
|
|
951
|
+
height: 100%;
|
|
952
|
+
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
|
953
|
+
transition: left 0.5s ease;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
.btn-sort-modern:hover {
|
|
957
|
+
transform: translateY(-2px);
|
|
958
|
+
box-shadow: 0 4px 16px rgba(46, 204, 113, 0.4);
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
.btn-sort-modern:hover::before {
|
|
962
|
+
left: 100%;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
.btn-sort-modern:active {
|
|
966
|
+
transform: translateY(0px);
|
|
967
|
+
box-shadow: 0 2px 8px rgba(46, 204, 113, 0.3);
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
.btn-content {
|
|
971
|
+
display: flex;
|
|
972
|
+
align-items: center;
|
|
973
|
+
gap: 8px;
|
|
974
|
+
position: relative;
|
|
975
|
+
z-index: 1;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
.btn-icon {
|
|
979
|
+
font-size: 16px;
|
|
980
|
+
opacity: 0.9;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
.btn-text {
|
|
984
|
+
font-size: 14px;
|
|
985
|
+
font-weight: 500;
|
|
986
|
+
white-space: nowrap;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
.btn-arrow {
|
|
990
|
+
font-size: 14px;
|
|
991
|
+
transition: transform 0.3s ease;
|
|
992
|
+
opacity: 0.8;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
.btn-sort-modern:hover .btn-arrow {
|
|
996
|
+
transform: scale(1.1);
|
|
997
|
+
opacity: 1;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/* Responsive design for sorting controls */
|
|
1001
|
+
@media (max-width: 768px) {
|
|
1002
|
+
.sorting-controls-modern {
|
|
1003
|
+
padding: 16px 20px;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
.sort-label-section {
|
|
1007
|
+
gap: 12px;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
.sort-icon-wrapper {
|
|
1011
|
+
width: 40px;
|
|
1012
|
+
height: 40px;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
.sort-icon-wrapper i {
|
|
1016
|
+
font-size: 16px;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
.sort-title {
|
|
1020
|
+
font-size: 14px;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
.sort-subtitle {
|
|
1024
|
+
font-size: 12px;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
.btn-sort-modern {
|
|
1028
|
+
padding: 10px 16px;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
.btn-text {
|
|
1032
|
+
font-size: 13px;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
@media (max-width: 576px) {
|
|
1037
|
+
.sorting-controls-modern {
|
|
1038
|
+
flex-direction: column;
|
|
1039
|
+
text-align: center;
|
|
1040
|
+
gap: 16px;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
.sorting-controls-modern .d-flex {
|
|
1044
|
+
flex-direction: column;
|
|
1045
|
+
gap: 16px;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
.sort-label-section {
|
|
1049
|
+
justify-content: center;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
/* Search Controls Styling */
|
|
1054
|
+
.search-controls-modern {
|
|
1055
|
+
background: #ffffff;
|
|
1056
|
+
border: 1px solid #e5e7eb;
|
|
1057
|
+
border-radius: 16px;
|
|
1058
|
+
padding: 20px 24px;
|
|
1059
|
+
margin-bottom: 20px;
|
|
1060
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
|
|
1061
|
+
transition: all 0.2s ease;
|
|
1062
|
+
position: relative;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
.search-controls-modern:hover {
|
|
1066
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.07), 0 2px 4px rgba(0, 0, 0, 0.06);
|
|
1067
|
+
border-color: #d1d5db;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
/* Property search container with outline */
|
|
1071
|
+
.property-search-container {
|
|
1072
|
+
position: relative;
|
|
1073
|
+
display: flex;
|
|
1074
|
+
align-items: center;
|
|
1075
|
+
border: 1px solid #d1d5db;
|
|
1076
|
+
border-radius: 6px;
|
|
1077
|
+
background-color: #f8f9fa;
|
|
1078
|
+
transition: all 0.2s ease;
|
|
1079
|
+
overflow: hidden;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
.property-search-container:hover {
|
|
1083
|
+
border-color: #9ca3af;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
.property-search-container:focus-within {
|
|
1087
|
+
border-color: #3b82f6;
|
|
1088
|
+
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
|
|
1089
|
+
background-color: #ffffff;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
/* Simple search input styling for Property Statistics */
|
|
1093
|
+
.property-stats-search-simple {
|
|
1094
|
+
border: none !important;
|
|
1095
|
+
box-shadow: none !important;
|
|
1096
|
+
background-color: transparent !important;
|
|
1097
|
+
border-radius: 0 !important;
|
|
1098
|
+
padding: 8px 12px !important;
|
|
1099
|
+
font-size: 14px;
|
|
1100
|
+
flex: 1;
|
|
1101
|
+
outline: none;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
.property-stats-search-simple:focus {
|
|
1105
|
+
background-color: transparent !important;
|
|
1106
|
+
box-shadow: none !important;
|
|
1107
|
+
border: none !important;
|
|
1108
|
+
outline: none;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
.property-stats-search-simple::placeholder {
|
|
1112
|
+
color: #9ca3af;
|
|
1113
|
+
font-style: italic;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
/* Search icon button */
|
|
1117
|
+
.property-search-icon-btn {
|
|
1118
|
+
background: none;
|
|
1119
|
+
border: none;
|
|
1120
|
+
padding: 8px 12px;
|
|
1121
|
+
color: #6b7280;
|
|
1122
|
+
cursor: pointer;
|
|
1123
|
+
display: flex;
|
|
1124
|
+
align-items: center;
|
|
1125
|
+
justify-content: center;
|
|
1126
|
+
transition: color 0.2s ease;
|
|
1127
|
+
border-left: 1px solid #e5e7eb;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
.property-search-icon-btn:hover {
|
|
1131
|
+
color: #3b82f6;
|
|
1132
|
+
background-color: rgba(59, 130, 246, 0.05);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
.property-search-icon-btn:active {
|
|
1136
|
+
color: #1d4ed8;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
/* Activities Coverage table styling */
|
|
1140
|
+
.table-activities thead {
|
|
1141
|
+
background-color: #007bff !important;
|
|
1142
|
+
color: white;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
.table-activities .sort-icon {
|
|
1146
|
+
color: #ffffff !important;
|
|
1147
|
+
text-shadow: 0 0 3px rgba(0,0,0,0.4);
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
.table-activities .sort-icon:hover {
|
|
1151
|
+
opacity: 1;
|
|
1152
|
+
transform: scale(1.2);
|
|
1153
|
+
text-shadow: 0 0 5px rgba(0,0,0,0.6);
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
.table-activities .activities-sort-icon {
|
|
1157
|
+
color: #ffffff !important;
|
|
1158
|
+
text-shadow: 0 0 3px rgba(0,0,0,0.4);
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
.table-activities .activities-sort-icon:hover {
|
|
1162
|
+
opacity: 1;
|
|
1163
|
+
transform: scale(1.2);
|
|
1164
|
+
text-shadow: 0 0 5px rgba(0,0,0,0.6);
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
/* Column widths for activities table */
|
|
1168
|
+
.table-activities th:nth-child(1), .table-activities td:nth-child(1) { /* Activity Name */
|
|
1169
|
+
width: 70%;
|
|
1170
|
+
min-width: 300px;
|
|
1171
|
+
text-align: left;
|
|
1172
|
+
padding-left: 180px;
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
.table-activities th:nth-child(2), .table-activities td:nth-child(2) { /* Visit Count */
|
|
1176
|
+
width: 30%;
|
|
1177
|
+
min-width: 150px;
|
|
1178
|
+
text-align: center;
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
/* Modern Activity Item Styling */
|
|
1182
|
+
.activity-item {
|
|
1183
|
+
background: #ffffff;
|
|
1184
|
+
border: 1px solid #f3f4f6;
|
|
1185
|
+
border-radius: 12px;
|
|
1186
|
+
padding: 16px 20px;
|
|
1187
|
+
margin-bottom: 8px;
|
|
1188
|
+
transition: all 0.2s ease;
|
|
1189
|
+
display: flex;
|
|
1190
|
+
align-items: center;
|
|
1191
|
+
justify-content: space-between;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
.activity-item:hover {
|
|
1195
|
+
border-color: #e5e7eb;
|
|
1196
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
|
1197
|
+
transform: translateY(-1px);
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
.activity-content {
|
|
1201
|
+
display: flex;
|
|
1202
|
+
align-items: center;
|
|
1203
|
+
gap: 12px;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
.activity-name {
|
|
1207
|
+
font-weight: 500;
|
|
1208
|
+
color: #374151;
|
|
1209
|
+
font-size: 14px;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
.traversal-badge {
|
|
1213
|
+
background: linear-gradient(135deg, #3b82f6, #1d4ed8) !important;
|
|
1214
|
+
border: none;
|
|
1215
|
+
border-radius: 20px;
|
|
1216
|
+
padding: 6px 12px;
|
|
1217
|
+
font-size: 12px;
|
|
1218
|
+
font-weight: 500;
|
|
1219
|
+
box-shadow: 0 2px 4px rgba(59, 130, 246, 0.2);
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
/* Modern Search Input */
|
|
1223
|
+
.activity-search-input {
|
|
1224
|
+
border: 1px solid #e5e7eb;
|
|
1225
|
+
border-radius: 12px;
|
|
1226
|
+
padding: 12px 16px;
|
|
1227
|
+
font-size: 14px;
|
|
1228
|
+
transition: all 0.2s ease;
|
|
1229
|
+
background: #f9fafb;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
.activity-search-input:focus {
|
|
1233
|
+
border-color: #3b82f6;
|
|
1234
|
+
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
|
1235
|
+
background: #ffffff;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
.search-btn {
|
|
1239
|
+
border-radius: 12px;
|
|
1240
|
+
padding: 12px 16px;
|
|
1241
|
+
background: linear-gradient(135deg, #3b82f6, #1d4ed8);
|
|
1242
|
+
border: none;
|
|
1243
|
+
transition: all 0.2s ease;
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
.search-btn:hover {
|
|
1247
|
+
background: linear-gradient(135deg, #2563eb, #1e40af);
|
|
1248
|
+
transform: translateY(-1px);
|
|
1249
|
+
box-shadow: 0 4px 8px rgba(59, 130, 246, 0.3);
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
/* Combined Search and Sort Controls */
|
|
1253
|
+
.search-sort-controls-modern {
|
|
1254
|
+
background: #ffffff;
|
|
1255
|
+
border: 1px solid #e5e7eb;
|
|
1256
|
+
border-radius: 16px;
|
|
1257
|
+
padding: 20px 24px;
|
|
1258
|
+
margin-bottom: 20px;
|
|
1259
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
|
|
1260
|
+
transition: all 0.2s ease;
|
|
1261
|
+
position: relative;
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
.search-sort-controls-modern:hover {
|
|
1265
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.07), 0 2px 4px rgba(0, 0, 0, 0.06);
|
|
1266
|
+
border-color: #d1d5db;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
.search-section {
|
|
1270
|
+
min-width: 0; /* Allow flex item to shrink */
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
.sort-section {
|
|
1274
|
+
flex-shrink: 0; /* Prevent sort section from shrinking */
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
.sort-label {
|
|
1278
|
+
white-space: nowrap;
|
|
1279
|
+
font-size: 14px;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
/* Responsive design for combined controls */
|
|
1283
|
+
@media (max-width: 768px) {
|
|
1284
|
+
.search-sort-controls-modern .d-flex {
|
|
1285
|
+
flex-direction: column;
|
|
1286
|
+
gap: 16px !important;
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
.search-section {
|
|
1290
|
+
width: 100% !important;
|
|
1291
|
+
max-width: none !important;
|
|
1292
|
+
flex-shrink: 1 !important;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
.sort-section {
|
|
1296
|
+
justify-content: center;
|
|
1297
|
+
width: 100%;
|
|
1298
|
+
margin-left: 0 !important;
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
@media (max-width: 576px) {
|
|
1303
|
+
.search-section {
|
|
1304
|
+
max-width: none !important;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
.search-icon-wrapper {
|
|
1308
|
+
width: 40px;
|
|
1309
|
+
height: 40px;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
.search-icon-wrapper i {
|
|
1313
|
+
font-size: 18px;
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
.search-icon-wrapper {
|
|
1318
|
+
width: 48px;
|
|
1319
|
+
height: 48px;
|
|
1320
|
+
background: linear-gradient(135deg, #17a2b8, #138496);
|
|
1321
|
+
border-radius: 12px;
|
|
1322
|
+
display: flex;
|
|
1323
|
+
align-items: center;
|
|
1324
|
+
justify-content: center;
|
|
1325
|
+
box-shadow: 0 2px 8px rgba(23, 162, 184, 0.3);
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
.search-icon-wrapper i {
|
|
1329
|
+
color: white;
|
|
1330
|
+
font-size: 20px;
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
.activity-search-input {
|
|
1334
|
+
border: 1px solid #ced4da;
|
|
1335
|
+
border-radius: 8px 0 0 8px;
|
|
1336
|
+
padding: 12px 16px;
|
|
1337
|
+
font-size: 14px;
|
|
1338
|
+
transition: all 0.3s ease;
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
.activity-search-input:focus {
|
|
1342
|
+
border-color: #17a2b8;
|
|
1343
|
+
box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.25);
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
.search-btn {
|
|
1347
|
+
border-radius: 0;
|
|
1348
|
+
border-left: none;
|
|
1349
|
+
border-right: none;
|
|
1350
|
+
padding: 12px 16px;
|
|
1351
|
+
transition: all 0.3s ease;
|
|
1352
|
+
background-color: #17a2b8;
|
|
1353
|
+
border-color: #17a2b8;
|
|
1354
|
+
color: white;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
.search-btn:hover {
|
|
1358
|
+
background-color: #138496;
|
|
1359
|
+
border-color: #117a8b;
|
|
1360
|
+
color: white;
|
|
1361
|
+
transform: translateY(-1px);
|
|
1362
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
.search-btn:focus {
|
|
1366
|
+
box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
.search-clear-btn {
|
|
1370
|
+
border-radius: 0 8px 8px 0;
|
|
1371
|
+
border-left: none;
|
|
1372
|
+
padding: 12px 16px;
|
|
1373
|
+
transition: all 0.3s ease;
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
.search-clear-btn:hover {
|
|
1377
|
+
background-color: #dc3545;
|
|
1378
|
+
border-color: #dc3545;
|
|
1379
|
+
color: white;
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
.search-results-count {
|
|
1383
|
+
display: block;
|
|
1384
|
+
margin-top: 8px;
|
|
1385
|
+
font-size: 12px;
|
|
1386
|
+
font-style: italic;
|
|
1387
|
+
}
|
|
1388
|
+
</style>
|
|
1389
|
+
</head>
|
|
1390
|
+
|
|
1391
|
+
<body>
|
|
1392
|
+
<!-- Header -->
|
|
1393
|
+
<header class="header text-center">
|
|
1394
|
+
<div class="container">
|
|
1395
|
+
<h1><i class="bi bi-bug"></i> Kea2 Test Report</h1>
|
|
1396
|
+
<p class="lead">Test Time: {{ test_time }}</p>
|
|
1397
|
+
<p class="lead">Log Stamp: {{ log_stamp }}</p>
|
|
1398
|
+
</div>
|
|
1399
|
+
</header>
|
|
1400
|
+
|
|
1401
|
+
<div class="container mb-5">
|
|
1402
|
+
<!-- Test Summary -->
|
|
1403
|
+
<div class="row mb-4">
|
|
1404
|
+
<div class="col-12">
|
|
1405
|
+
<div class="summary-card">
|
|
1406
|
+
<h2 class="section-title">Test Summary</h2>
|
|
1407
|
+
<div class="summary-grid">
|
|
1408
|
+
<div class="summary-item">
|
|
1409
|
+
<i class="bi bi-bug text-danger"></i>
|
|
1410
|
+
<span class="stat-value value-danger">{{ bugs_found }}</span>
|
|
1411
|
+
<span class="stat-label">Property Violations</span>
|
|
1412
|
+
</div>
|
|
1413
|
+
<div class="summary-item">
|
|
1414
|
+
<i class="bi bi-shield-exclamation text-warning"></i>
|
|
1415
|
+
<span class="stat-value value-warning">{{ invariant_violations_count }}</span>
|
|
1416
|
+
<span class="stat-label">Invariant Violations</span>
|
|
1417
|
+
</div>
|
|
1418
|
+
<div class="summary-item">
|
|
1419
|
+
<i class="bi bi-clock-history text-primary"></i>
|
|
1420
|
+
<span class="stat-value value-highlight">{{ total_testing_time }}</span>
|
|
1421
|
+
<span class="stat-label">Total Testing Time</span>
|
|
1422
|
+
</div>
|
|
1423
|
+
<div class="summary-item">
|
|
1424
|
+
<i class="bi bi-activity text-success"></i>
|
|
1425
|
+
<span class="stat-value value-success">{{ executed_events }}</span>
|
|
1426
|
+
<span class="stat-label">Executed Events</span>
|
|
1427
|
+
</div>
|
|
1428
|
+
<div class="summary-item">
|
|
1429
|
+
<i class="bi bi-pie-chart text-warning"></i>
|
|
1430
|
+
<span class="stat-value value-warning">{{ coverage_percent }}%</span>
|
|
1431
|
+
<span class="stat-label">Activity Coverage</span>
|
|
1432
|
+
</div>
|
|
1433
|
+
<div class="summary-item">
|
|
1434
|
+
<i class="bi bi-list-check text-info"></i>
|
|
1435
|
+
<span class="stat-value value-highlight">{{ executed_properties_count }}/{{ all_properties_count }}</span>
|
|
1436
|
+
<span class="stat-label">Executed Properties</span>
|
|
1437
|
+
</div>
|
|
1438
|
+
<div class="summary-item">
|
|
1439
|
+
<i class="bi bi-exclamation-octagon text-danger"></i>
|
|
1440
|
+
<span class="stat-value value-danger">{{ triggered_crash_count }}</span>
|
|
1441
|
+
<span class="stat-label">Triggered Crash</span>
|
|
1442
|
+
</div>
|
|
1443
|
+
<div class="summary-item">
|
|
1444
|
+
<i class="bi bi-hourglass-split text-warning"></i>
|
|
1445
|
+
<span class="stat-value value-warning">{{ triggered_anr_count }}</span>
|
|
1446
|
+
<span class="stat-label">Triggered ANR</span>
|
|
1447
|
+
</div>
|
|
1448
|
+
</div>
|
|
1449
|
+
</div>
|
|
1450
|
+
</div>
|
|
1451
|
+
</div>
|
|
1452
|
+
|
|
1453
|
+
<!-- Coverage Trend Chart -->
|
|
1454
|
+
<div class="section-block">
|
|
1455
|
+
<h2 class="section-title">Coverage Trend</h2>
|
|
1456
|
+
<div class="chart-container">
|
|
1457
|
+
<canvas id="coverageChart"></canvas>
|
|
1458
|
+
</div>
|
|
1459
|
+
</div>
|
|
1460
|
+
|
|
1461
|
+
<!-- Property Execution Trend Chart -->
|
|
1462
|
+
<div class="section-block">
|
|
1463
|
+
<h2 class="section-title">Property Execution Trend</h2>
|
|
1464
|
+
<div class="chart-container">
|
|
1465
|
+
<canvas id="propertyExecutionChart"></canvas>
|
|
1466
|
+
</div>
|
|
1467
|
+
</div>
|
|
1468
|
+
|
|
1469
|
+
<!-- Activities Coverage -->
|
|
1470
|
+
<div class="section-block">
|
|
1471
|
+
<h2 class="section-title">Activities Coverage</h2>
|
|
1472
|
+
|
|
1473
|
+
<!-- Search Controls for Activities -->
|
|
1474
|
+
<div class="mb-4">
|
|
1475
|
+
<div class="d-flex align-items-center">
|
|
1476
|
+
<div style="width: 33%;">
|
|
1477
|
+
<div class="property-search-container">
|
|
1478
|
+
<input type="text" class="form-control property-search-input property-stats-search-simple"
|
|
1479
|
+
id="activities-search"
|
|
1480
|
+
placeholder="Search activities by name..."
|
|
1481
|
+
data-target="activities-container"
|
|
1482
|
+
data-item-class="activity-row"
|
|
1483
|
+
data-pagination="activities-pagination"
|
|
1484
|
+
data-page-size="activities-page-size">
|
|
1485
|
+
<button class="property-search-icon-btn" type="button" id="activities-search-btn">
|
|
1486
|
+
<i class="bi bi-search"></i>
|
|
1487
|
+
</button>
|
|
1488
|
+
</div>
|
|
1489
|
+
</div>
|
|
1490
|
+
<div class="ms-3">
|
|
1491
|
+
<small class="text-muted search-results-count" id="activities-search-results"></small>
|
|
1492
|
+
</div>
|
|
1493
|
+
</div>
|
|
1494
|
+
</div>
|
|
1495
|
+
|
|
1496
|
+
<div class="table-responsive">
|
|
1497
|
+
<table class="table table-custom table-activities">
|
|
1498
|
+
<thead>
|
|
1499
|
+
<tr>
|
|
1500
|
+
<th>Activity Name <span class="badge bg-primary ms-2" style="font-size: 0.9rem; font-weight: 600;">{{ tested_activities_count }}/{{ total_activities_count }}</span></th>
|
|
1501
|
+
<th>Visit Count <i class="bi bi-arrow-down-up text-muted sort-icon activities-sort-icon" id="visit-count-sort" data-column="visit-count" data-order="none" style="cursor: pointer;"></i></th>
|
|
1502
|
+
</tr>
|
|
1503
|
+
</thead>
|
|
1504
|
+
<tbody id="activities-container">
|
|
1505
|
+
{% if total_activities|length > 0 %}
|
|
1506
|
+
{% for activity in total_activities %}
|
|
1507
|
+
<tr class="activity-row" data-page="1"
|
|
1508
|
+
data-activity-name="{{ activity }}"
|
|
1509
|
+
data-visit-count="{{ activity_count_history[activity] if activity in activity_count_history else 0 }}">
|
|
1510
|
+
<td>
|
|
1511
|
+
{% if activity in tested_activities %}
|
|
1512
|
+
<i class="bi bi-check-circle-fill text-success me-2"></i>
|
|
1513
|
+
{% else %}
|
|
1514
|
+
<i class="bi bi-dash-circle text-secondary me-2"></i>
|
|
1515
|
+
{% endif %}
|
|
1516
|
+
<span class="activity-name">{{ activity }}</span>
|
|
1517
|
+
</td>
|
|
1518
|
+
<td>
|
|
1519
|
+
{% if activity in activity_count_history %}
|
|
1520
|
+
<span class="badge bg-info text-white">
|
|
1521
|
+
<i class="bi bi-eye"></i> {{ activity_count_history[activity] }} times
|
|
1522
|
+
</span>
|
|
1523
|
+
{% else %}
|
|
1524
|
+
<span class="badge bg-secondary text-white">
|
|
1525
|
+
<i class="bi bi-dash"></i> 0 times
|
|
1526
|
+
</span>
|
|
1527
|
+
{% endif %}
|
|
1528
|
+
</td>
|
|
1529
|
+
</tr>
|
|
1530
|
+
{% endfor %}
|
|
1531
|
+
{% else %}
|
|
1532
|
+
<tr>
|
|
1533
|
+
<td colspan="2" class="text-center">
|
|
1534
|
+
<div class="alert alert-warning mb-0">
|
|
1535
|
+
No activities information available
|
|
1536
|
+
</div>
|
|
1537
|
+
</td>
|
|
1538
|
+
</tr>
|
|
1539
|
+
{% endif %}
|
|
1540
|
+
</tbody>
|
|
1541
|
+
</table>
|
|
1542
|
+
|
|
1543
|
+
<!-- Pagination for Activities -->
|
|
1544
|
+
<div class="d-flex justify-content-between align-items-center mt-3">
|
|
1545
|
+
<div class="d-flex align-items-center">
|
|
1546
|
+
<label for="activities-page-size" class="form-label me-2 mb-0">Show:</label>
|
|
1547
|
+
<select class="form-select form-select-sm" id="activities-page-size" style="width: auto;">
|
|
1548
|
+
<option value="5">5</option>
|
|
1549
|
+
<option value="10" selected>10</option>
|
|
1550
|
+
<option value="20">20</option>
|
|
1551
|
+
<option value="50">50</option>
|
|
1552
|
+
<option value="100">100</option>
|
|
1553
|
+
</select>
|
|
1554
|
+
</div>
|
|
1555
|
+
<nav aria-label="Activities Pagination">
|
|
1556
|
+
<ul class="pagination pagination-sm mb-0" id="activities-pagination">
|
|
1557
|
+
<!-- Pagination will be generated by JavaScript -->
|
|
1558
|
+
</ul>
|
|
1559
|
+
</nav>
|
|
1560
|
+
</div>
|
|
1561
|
+
</div>
|
|
1562
|
+
</div>
|
|
1563
|
+
|
|
1564
|
+
<!-- Screenshots Section -->
|
|
1565
|
+
{% if take_screenshots %}
|
|
1566
|
+
{% if screenshots or kill_apps_events %}
|
|
1567
|
+
<div class="section-block">
|
|
1568
|
+
<h2 class="section-title">Test Screenshots</h2>
|
|
1569
|
+
<div class="card">
|
|
1570
|
+
<div class="card-body">
|
|
1571
|
+
<div class="screenshots-container" id="screenshots">
|
|
1572
|
+
{% if screenshots %}
|
|
1573
|
+
{% for screenshot in screenshots %}
|
|
1574
|
+
<div class="screenshot-item">
|
|
1575
|
+
{% if screenshot.path %}
|
|
1576
|
+
<a href="{{ screenshot.path }}" target="_blank">
|
|
1577
|
+
<img src="{{ screenshot.path }}" class="screenshot-img" id="{{ screenshot.id }}">
|
|
1578
|
+
</a>
|
|
1579
|
+
{% else %}
|
|
1580
|
+
<div class="screenshot-placeholder" id="{{ screenshot.id }}">
|
|
1581
|
+
{% if screenshot.info == "kill_apps" %}
|
|
1582
|
+
<div class="placeholder-content">
|
|
1583
|
+
<span class="placeholder-icon reboot">
|
|
1584
|
+
<i class="bi bi-bootstrap-reboot"></i>
|
|
1585
|
+
</span>
|
|
1586
|
+
<div class="placeholder-text">Restart app</div>
|
|
1587
|
+
</div>
|
|
1588
|
+
{% else %}
|
|
1589
|
+
<div class="placeholder-content">
|
|
1590
|
+
<span class="placeholder-icon info">
|
|
1591
|
+
<i class="bi bi-info-circle"></i>
|
|
1592
|
+
</span>
|
|
1593
|
+
<div class="placeholder-text">Info event</div>
|
|
1594
|
+
</div>
|
|
1595
|
+
{% endif %}
|
|
1596
|
+
</div>
|
|
1597
|
+
{% endif %}
|
|
1598
|
+
<div class="screenshot-caption">{{ screenshot.caption }}</div>
|
|
1599
|
+
</div>
|
|
1600
|
+
{% endfor %}
|
|
1601
|
+
{% else %}
|
|
1602
|
+
{% for ev in kill_apps_events %}
|
|
1603
|
+
<div class="screenshot-item">
|
|
1604
|
+
<div class="screenshot-placeholder" id="{{ ev.step_index }}">
|
|
1605
|
+
<div class="placeholder-content">
|
|
1606
|
+
<span class="placeholder-icon reboot">
|
|
1607
|
+
<i class="bi bi-bootstrap-reboot"></i>
|
|
1608
|
+
</span>
|
|
1609
|
+
<div class="placeholder-text">Restart app</div>
|
|
1610
|
+
</div>
|
|
1611
|
+
</div>
|
|
1612
|
+
<div class="screenshot-caption">
|
|
1613
|
+
{{ ev.step_index }}. Monkey Step {{ ev.monkey_steps_count }}: restart app
|
|
1614
|
+
</div>
|
|
1615
|
+
</div>
|
|
1616
|
+
{% endfor %}
|
|
1617
|
+
{% endif %}
|
|
1618
|
+
</div>
|
|
1619
|
+
</div>
|
|
1620
|
+
</div>
|
|
1621
|
+
</div>
|
|
1622
|
+
{% else %}
|
|
1623
|
+
<div class="section-block">
|
|
1624
|
+
<h2 class="section-title">Test Screenshots</h2>
|
|
1625
|
+
<div class="alert alert-info text-center">
|
|
1626
|
+
<i class="bi bi-info-circle"></i> No screenshots captured in this test session.
|
|
1627
|
+
</div>
|
|
1628
|
+
</div>
|
|
1629
|
+
{% endif %}
|
|
1630
|
+
{% endif %}
|
|
1631
|
+
|
|
1632
|
+
<!-- Crash Analysis Section -->
|
|
1633
|
+
{% if crash_events or anr_events %}
|
|
1634
|
+
<div class="section-block">
|
|
1635
|
+
<h2 class="section-title">
|
|
1636
|
+
<i class="bi bi-exclamation-triangle text-danger"></i> Crash and ANR Events
|
|
1637
|
+
</h2>
|
|
1638
|
+
|
|
1639
|
+
<!-- Detailed Crash Information -->
|
|
1640
|
+
<div class="crash-analysis-panel">
|
|
1641
|
+
<!-- Event Filter -->
|
|
1642
|
+
<div class="mb-3">
|
|
1643
|
+
<div class="btn-group" role="group" aria-label="Event filter">
|
|
1644
|
+
<input type="radio" class="btn-check" name="event-filter" id="all-events" autocomplete="off" checked>
|
|
1645
|
+
<label class="btn btn-outline-primary" for="all-events">All Events ({{ (crash_events|length) + (anr_events|length) }})</label>
|
|
1646
|
+
|
|
1647
|
+
<input type="radio" class="btn-check" name="event-filter" id="crashes-only" autocomplete="off">
|
|
1648
|
+
<label class="btn btn-outline-danger" for="crashes-only">Crashes Only ({{ crash_events|length }})</label>
|
|
1649
|
+
|
|
1650
|
+
<input type="radio" class="btn-check" name="event-filter" id="anr-only" autocomplete="off">
|
|
1651
|
+
<label class="btn btn-outline-warning" for="anr-only">ANR Only ({{ anr_events|length }})</label>
|
|
1652
|
+
</div>
|
|
1653
|
+
</div>
|
|
1654
|
+
|
|
1655
|
+
<!-- Events Table -->
|
|
1656
|
+
<div class="table-responsive">
|
|
1657
|
+
<table class="table table-custom">
|
|
1658
|
+
<thead>
|
|
1659
|
+
<tr>
|
|
1660
|
+
<th>Type</th>
|
|
1661
|
+
<th>Time</th>
|
|
1662
|
+
<th>Exception</th>
|
|
1663
|
+
<th>Process</th>
|
|
1664
|
+
{% if take_screenshots %}
|
|
1665
|
+
<th>Interaction Scenario Pages</th>
|
|
1666
|
+
{% endif %}
|
|
1667
|
+
<th>Details</th>
|
|
1668
|
+
</tr>
|
|
1669
|
+
</thead>
|
|
1670
|
+
<tbody id="crash-events-container">
|
|
1671
|
+
{% for crash in crash_events %}
|
|
1672
|
+
<tr class="event-row" data-type="crash" data-page="1">
|
|
1673
|
+
<td><span class="badge bg-danger">CRASH</span></td>
|
|
1674
|
+
<td>{{ crash.time }}</td>
|
|
1675
|
+
<td>{{ crash.exception_type }}</td>
|
|
1676
|
+
<td>{{ crash.process }}</td>
|
|
1677
|
+
{% if take_screenshots %}
|
|
1678
|
+
<td>
|
|
1679
|
+
{% if crash.screenshot_id %}
|
|
1680
|
+
<a href="#{{ crash.screenshot_id }}" class="link-button" onclick="scrollToScreenshot('{{ crash.screenshot_id }}')">
|
|
1681
|
+
<i class="bi bi-camera"></i> Screenshot {{ crash.screenshot_id }}
|
|
1682
|
+
</a>
|
|
1683
|
+
{% else %}
|
|
1684
|
+
<span class="text-muted">No screenshot</span>
|
|
1685
|
+
{% endif %}
|
|
1686
|
+
</td>
|
|
1687
|
+
{% endif %}
|
|
1688
|
+
<td>
|
|
1689
|
+
<button class="btn btn-sm btn-outline-primary" type="button"
|
|
1690
|
+
data-bs-toggle="collapse" data-bs-target="#crash-detail-{{ loop.index }}"
|
|
1691
|
+
aria-expanded="false" aria-controls="crash-detail-{{ loop.index }}">
|
|
1692
|
+
<i class="bi bi-eye"></i> Details
|
|
1693
|
+
</button>
|
|
1694
|
+
<button class="btn btn-sm btn-outline-secondary copy-stack-btn"
|
|
1695
|
+
data-stack-index="{{ loop.index }}">
|
|
1696
|
+
<i class="bi bi-clipboard"></i> Copy
|
|
1697
|
+
</button>
|
|
1698
|
+
</td>
|
|
1699
|
+
</tr>
|
|
1700
|
+
<tr class="collapse" id="crash-detail-{{ loop.index }}">
|
|
1701
|
+
<td colspan="{% if take_screenshots %}6{% else %}5{% endif %}">
|
|
1702
|
+
<div class="bg-light p-3 rounded">
|
|
1703
|
+
<h6 class="text-danger">Stack Trace:</h6>
|
|
1704
|
+
<pre class="text-danger mb-0 text-start" id="stack-trace-{{ loop.index }}" style="font-size: 0.9em; white-space: pre-wrap; text-align: left;">{{ crash.stack_trace }}</pre>
|
|
1705
|
+
</div>
|
|
1706
|
+
</td>
|
|
1707
|
+
</tr>
|
|
1708
|
+
{% endfor %}
|
|
1709
|
+
|
|
1710
|
+
{% for anr in anr_events %}
|
|
1711
|
+
<tr class="event-row" data-type="anr" data-page="1">
|
|
1712
|
+
<td><span class="badge bg-warning text-dark">ANR</span></td>
|
|
1713
|
+
<td>{{ anr.time }}</td>
|
|
1714
|
+
<td>{{ anr.reason }}</td>
|
|
1715
|
+
<td>{{ anr.process }}</td>
|
|
1716
|
+
{% if take_screenshots %}
|
|
1717
|
+
<td>
|
|
1718
|
+
{% if anr.screenshot_id %}
|
|
1719
|
+
<a href="#{{ anr.screenshot_id }}" class="link-button" onclick="scrollToScreenshot('{{ anr.screenshot_id }}')">
|
|
1720
|
+
<i class="bi bi-camera"></i> Screenshot {{ anr.screenshot_id }}
|
|
1721
|
+
</a>
|
|
1722
|
+
{% else %}
|
|
1723
|
+
<span class="text-muted">No screenshot</span>
|
|
1724
|
+
{% endif %}
|
|
1725
|
+
</td>
|
|
1726
|
+
{% endif %}
|
|
1727
|
+
<td>
|
|
1728
|
+
<button class="btn btn-sm btn-outline-primary" type="button"
|
|
1729
|
+
data-bs-toggle="collapse" data-bs-target="#anr-detail-{{ loop.index }}"
|
|
1730
|
+
aria-expanded="false" aria-controls="anr-detail-{{ loop.index }}">
|
|
1731
|
+
<i class="bi bi-eye"></i> Details
|
|
1732
|
+
</button>
|
|
1733
|
+
<button class="btn btn-sm btn-outline-secondary copy-stack-btn"
|
|
1734
|
+
data-stack-index="anr-{{ loop.index }}">
|
|
1735
|
+
<i class="bi bi-clipboard"></i> Copy
|
|
1736
|
+
</button>
|
|
1737
|
+
</td>
|
|
1738
|
+
</tr>
|
|
1739
|
+
<tr class="collapse" id="anr-detail-{{ loop.index }}">
|
|
1740
|
+
<td colspan="{% if take_screenshots %}6{% else %}5{% endif %}">
|
|
1741
|
+
<div class="bg-light p-3 rounded">
|
|
1742
|
+
<h6 class="text-dark">ANR Details:</h6>
|
|
1743
|
+
<pre class="text-dark mb-0 text-start" id="stack-trace-anr-{{ loop.index }}" style="font-size: 0.9em; white-space: pre-wrap; text-align: left;">{{ anr.trace }}</pre>
|
|
1744
|
+
</div>
|
|
1745
|
+
</td>
|
|
1746
|
+
</tr>
|
|
1747
|
+
{% endfor %}
|
|
1748
|
+
</tbody>
|
|
1749
|
+
</table>
|
|
1750
|
+
</div>
|
|
1751
|
+
|
|
1752
|
+
<!-- Pagination for Crash Events -->
|
|
1753
|
+
<div class="d-flex justify-content-between align-items-center mt-3 pagination-container pagination-violations">
|
|
1754
|
+
<div class="d-flex align-items-center gap-3">
|
|
1755
|
+
<label for="events-page-size" class="form-label me-2 mb-0">Show:</label>
|
|
1756
|
+
<select class="form-select form-select-sm" id="events-page-size" style="width: auto;">
|
|
1757
|
+
<option value="5">5</option>
|
|
1758
|
+
<option value="10" selected>10</option>
|
|
1759
|
+
<option value="20">20</option>
|
|
1760
|
+
<option value="50">50</option>
|
|
1761
|
+
<option value="100">100</option>
|
|
1762
|
+
</select>
|
|
1763
|
+
</div>
|
|
1764
|
+
<nav aria-label="Crash Events Pagination">
|
|
1765
|
+
<ul class="pagination pagination-sm mb-0" id="events-pagination">
|
|
1766
|
+
<!-- Pagination will be generated by JavaScript -->
|
|
1767
|
+
</ul>
|
|
1768
|
+
</nav>
|
|
1769
|
+
</div>
|
|
1770
|
+
</div>
|
|
1771
|
+
</div>
|
|
1772
|
+
{% else %}
|
|
1773
|
+
<div class="section-block">
|
|
1774
|
+
<h2 class="section-title">
|
|
1775
|
+
<i class="bi bi-exclamation-triangle text-danger"></i> Crash and ANR Events
|
|
1776
|
+
</h2>
|
|
1777
|
+
<div class="alert alert-info text-center">
|
|
1778
|
+
<i class="bi bi-info-circle"></i> No crash or ANR events detected in this test session.
|
|
1779
|
+
</div>
|
|
1780
|
+
</div>
|
|
1781
|
+
{% endif %}
|
|
1782
|
+
|
|
1783
|
+
<!-- Property Violations List -->
|
|
1784
|
+
{% if take_screenshots %}
|
|
1785
|
+
{% if property_violations %}
|
|
1786
|
+
<div class="section-block">
|
|
1787
|
+
<h2 class="section-title">Property Violations</h2>
|
|
1788
|
+
<div class="table-responsive">
|
|
1789
|
+
<table class="table table-custom table-violations">
|
|
1790
|
+
<thead>
|
|
1791
|
+
<tr>
|
|
1792
|
+
<th>Index</th>
|
|
1793
|
+
<th>Property Name</th>
|
|
1794
|
+
<th>Interaction Scenario Pages</th>
|
|
1795
|
+
</tr>
|
|
1796
|
+
</thead>
|
|
1797
|
+
<tbody id="property-violations-container">
|
|
1798
|
+
{% for violation in property_violations %}
|
|
1799
|
+
<tr class="property-violation-row" data-page="1" data-status="{{ violation.state|default('fail') }}" data-kind="{{ violation.kind|default('unknown') }}">
|
|
1800
|
+
<td>{{ violation.index }}</td>
|
|
1801
|
+
<td><span class="badge bg-light text-dark badge-custom">{{ violation.property_name }}</span></td>
|
|
1802
|
+
<td>
|
|
1803
|
+
<a href="#{{ violation.interaction_pages|last }}" class="link-button"
|
|
1804
|
+
onclick="scrollToScreenshot('{{ violation.interaction_pages|last }}')">
|
|
1805
|
+
{{ violation.interaction_pages[0] }} ~ {{ violation.interaction_pages[1] }}
|
|
1806
|
+
</a>
|
|
1807
|
+
</td>
|
|
1808
|
+
</tr>
|
|
1809
|
+
{% endfor %}
|
|
1810
|
+
</tbody>
|
|
1811
|
+
</table>
|
|
1812
|
+
|
|
1813
|
+
<!-- Pagination for Property Violations -->
|
|
1814
|
+
<div class="d-flex justify-content-between align-items-center mt-3 pagination-container pagination-violations">
|
|
1815
|
+
<div class="d-flex align-items-center gap-3">
|
|
1816
|
+
<label for="violations-page-size" class="form-label me-2 mb-0">Show:</label>
|
|
1817
|
+
<select class="form-select form-select-sm" id="violations-page-size" style="width: auto;">
|
|
1818
|
+
<option value="5">5</option>
|
|
1819
|
+
<option value="10" selected>10</option>
|
|
1820
|
+
<option value="20">20</option>
|
|
1821
|
+
<option value="50">50</option>
|
|
1822
|
+
<option value="100">100</option>
|
|
1823
|
+
</select>
|
|
1824
|
+
<label for="violations-status-filter" class="form-label mb-0">Status:</label>
|
|
1825
|
+
<select class="form-select form-select-sm" id="violations-status-filter" style="width: auto;">
|
|
1826
|
+
<option value="all" selected>All</option>
|
|
1827
|
+
<option value="fail">Fail only</option>
|
|
1828
|
+
</select>
|
|
1829
|
+
<label for="violations-kind-filter" class="form-label mb-0">Kind:</label>
|
|
1830
|
+
<select class="form-select form-select-sm" id="violations-kind-filter" style="width: auto;">
|
|
1831
|
+
<option value="all" selected>All</option>
|
|
1832
|
+
<option value="property">Property</option>
|
|
1833
|
+
<option value="invariant">Invariant</option>
|
|
1834
|
+
</select>
|
|
1835
|
+
</div>
|
|
1836
|
+
<nav aria-label="Property Violations Pagination">
|
|
1837
|
+
<ul class="pagination pagination-sm mb-0" id="violations-pagination">
|
|
1838
|
+
<!-- Pagination will be generated by JavaScript -->
|
|
1839
|
+
</ul>
|
|
1840
|
+
</nav>
|
|
1841
|
+
</div>
|
|
1842
|
+
</div>
|
|
1843
|
+
</div>
|
|
1844
|
+
{% else %}
|
|
1845
|
+
<div class="section-block">
|
|
1846
|
+
<h2 class="section-title">Property Violations</h2>
|
|
1847
|
+
<div class="alert alert-info text-center">
|
|
1848
|
+
<i class="bi bi-info-circle"></i> No property violations detected in this test session.
|
|
1849
|
+
</div>
|
|
1850
|
+
</div>
|
|
1851
|
+
{% endif %}
|
|
1852
|
+
{% endif %}
|
|
1853
|
+
|
|
1854
|
+
<!-- Property Checking Statistics -->
|
|
1855
|
+
<div class="section-block">
|
|
1856
|
+
<h2 class="section-title">Property Checking Statistics</h2>
|
|
1857
|
+
|
|
1858
|
+
<!-- Search Controls for Property Statistics -->
|
|
1859
|
+
<div class="mb-4">
|
|
1860
|
+
<div class="d-flex align-items-center">
|
|
1861
|
+
<div style="width: 33%;">
|
|
1862
|
+
<div class="property-search-container">
|
|
1863
|
+
<input type="text" class="form-control property-search-input property-stats-search-simple"
|
|
1864
|
+
id="property-stats-search"
|
|
1865
|
+
placeholder="Search properties by name..."
|
|
1866
|
+
data-target="property-stats-container"
|
|
1867
|
+
data-item-class="property-stat-row"
|
|
1868
|
+
data-pagination="stats-pagination"
|
|
1869
|
+
data-page-size="stats-page-size">
|
|
1870
|
+
<button class="property-search-icon-btn" type="button" id="property-search-btn">
|
|
1871
|
+
<i class="bi bi-search"></i>
|
|
1872
|
+
</button>
|
|
1873
|
+
</div>
|
|
1874
|
+
</div>
|
|
1875
|
+
<div class="ms-3">
|
|
1876
|
+
<small class="text-muted search-results-count" id="property-search-results"></small>
|
|
1877
|
+
</div>
|
|
1878
|
+
</div>
|
|
1879
|
+
</div>
|
|
1880
|
+
|
|
1881
|
+
<div class="mb-3">
|
|
1882
|
+
<div class="btn-group" role="group" aria-label="Property kind filter">
|
|
1883
|
+
<input type="radio" class="btn-check" name="property-kind-filter" id="property-kind-all" autocomplete="off" checked>
|
|
1884
|
+
<label class="btn btn-outline-primary" for="property-kind-all">All ({{ property_kind_summary.all|default(0) }})</label>
|
|
1885
|
+
|
|
1886
|
+
<input type="radio" class="btn-check" name="property-kind-filter" id="property-kind-property" autocomplete="off">
|
|
1887
|
+
<label class="btn btn-outline-info" for="property-kind-property">Property ({{ property_kind_summary.property|default(0) }})</label>
|
|
1888
|
+
|
|
1889
|
+
<input type="radio" class="btn-check" name="property-kind-filter" id="property-kind-invariant" autocomplete="off">
|
|
1890
|
+
<label class="btn btn-outline-warning" for="property-kind-invariant">Invariant ({{ property_kind_summary.invariant|default(0) }})</label>
|
|
1891
|
+
</div>
|
|
1892
|
+
</div>
|
|
1893
|
+
|
|
1894
|
+
<div class="table-responsive">
|
|
1895
|
+
<table class="table table-custom table-property-stats">
|
|
1896
|
+
<thead>
|
|
1897
|
+
<tr>
|
|
1898
|
+
<th>Index</th>
|
|
1899
|
+
<th>Property Name <span class="badge bg-primary ms-2" id="property-stats-total-properties" style="font-size: 0.9rem; font-weight: 600;">{{ property_stats_summary.total_properties }}</span></th>
|
|
1900
|
+
<th>Precondition Satisfied <span class="badge bg-success ms-2" id="property-stats-total-precond" style="font-size: 0.9rem; font-weight: 600;">{{ property_stats_summary.total_precond_satisfied }}</span></th>
|
|
1901
|
+
<th>Executed <span class="badge bg-info ms-2" id="property-stats-total-executed" style="font-size: 0.9rem; font-weight: 600;">{{ property_stats_summary.total_executed }}</span></th>
|
|
1902
|
+
<th>Passes <span class="badge bg-secondary ms-2" id="property-stats-total-pass" style="font-size: 0.9rem; font-weight: 600;">{{ property_stats_summary.total_passes }}</span></th>
|
|
1903
|
+
<th>Fails <span class="badge bg-danger ms-2" id="property-stats-total-fail" style="font-size: 0.9rem; font-weight: 600;">{{ property_stats_summary.total_fails }}</span> <i class="bi bi-arrow-down-up text-muted sort-icon" id="fails-sort" data-column="fails" data-order="none" style="cursor: pointer;"></i></th>
|
|
1904
|
+
<th>Errors <span class="badge bg-warning ms-2" id="property-stats-total-error" style="font-size: 0.9rem; font-weight: 600;">{{ property_stats_summary.total_errors }}</span> <i class="bi bi-arrow-down-up text-muted sort-icon" id="errors-sort" data-column="errors" data-order="none" style="cursor: pointer;"></i></th>
|
|
1905
|
+
<th>Error Details</th>
|
|
1906
|
+
</tr>
|
|
1907
|
+
</thead>
|
|
1908
|
+
<tbody id="property-stats-container">
|
|
1909
|
+
{% for property_name, test_result in property_stats.items() %}
|
|
1910
|
+
<tr class="property-stat-row" data-page="1"
|
|
1911
|
+
data-index="{{ loop.index }}"
|
|
1912
|
+
data-property-name="{{ property_name }}"
|
|
1913
|
+
data-kind="{{ test_result.kind|default('unknown') }}"
|
|
1914
|
+
data-precond-satisfied="{{ test_result.precond_satisfied|default(0) }}"
|
|
1915
|
+
data-executed="{{ test_result.executed|default(0) }}"
|
|
1916
|
+
data-pass="{{ test_result.pass_count|default(0) }}"
|
|
1917
|
+
data-fails="{{ test_result.fail|default(0) }}"
|
|
1918
|
+
data-errors="{{ test_result.error|default(0) }}">
|
|
1919
|
+
<td>{{ loop.index }}</td>
|
|
1920
|
+
<td>
|
|
1921
|
+
<span class="badge bg-light text-dark badge-custom">{{ property_name }}</span>
|
|
1922
|
+
{% set kind_label = test_result.kind|default('unknown') %}
|
|
1923
|
+
{% set is_invariant = kind_label == 'invariant' %}
|
|
1924
|
+
<span class="badge ms-1 {% if kind_label == 'property' %}bg-info text-dark{% elif kind_label == 'invariant' %}bg-warning text-dark{% else %}bg-secondary text-white{% endif %}">
|
|
1925
|
+
{{ kind_label|capitalize }}
|
|
1926
|
+
</span>
|
|
1927
|
+
</td>
|
|
1928
|
+
<td>{{ '/' if is_invariant else test_result.precond_satisfied|default(0) }}</td>
|
|
1929
|
+
<td>{{ '/' if is_invariant else test_result.executed|default(0) }}</td>
|
|
1930
|
+
<td>{{ '/' if is_invariant else test_result.pass_count|default(0) }}</td>
|
|
1931
|
+
<td><span class="badge bg-danger text-white">{{ test_result.fail|default(0) }}</span></td>
|
|
1932
|
+
<td><span class="badge bg-warning text-dark">{{ test_result.error|default(0) }}</span></td>
|
|
1933
|
+
<td>
|
|
1934
|
+
{% if (test_result.fail|default(0) > 0 or test_result.error|default(0) > 0) and property_name in property_error_details %}
|
|
1935
|
+
{% set error_list = property_error_details[property_name] %}
|
|
1936
|
+
{% set property_index = loop.index %}
|
|
1937
|
+
{% if error_list|length == 1 %}
|
|
1938
|
+
<!-- Single error - simple display -->
|
|
1939
|
+
<button class="btn btn-sm btn-outline-danger" type="button" data-bs-toggle="collapse"
|
|
1940
|
+
data-bs-target="#single-error-detail-{{ property_index }}" aria-expanded="false"
|
|
1941
|
+
aria-controls="single-error-detail-{{ property_index }}">
|
|
1942
|
+
<i class="bi bi-exclamation-triangle"></i> View Error
|
|
1943
|
+
</button>
|
|
1944
|
+
{% else %}
|
|
1945
|
+
<!-- Multiple errors - tabbed display -->
|
|
1946
|
+
<button class="btn btn-sm btn-outline-danger" type="button" data-bs-toggle="collapse"
|
|
1947
|
+
data-bs-target="#multi-error-detail-{{ property_index }}" aria-expanded="false"
|
|
1948
|
+
aria-controls="multi-error-detail-{{ property_index }}">
|
|
1949
|
+
<i class="bi bi-exclamation-triangle"></i> View {{ error_list|length }} Errors
|
|
1950
|
+
</button>
|
|
1951
|
+
{% endif %}
|
|
1952
|
+
{% else %}
|
|
1953
|
+
<span class="text-muted">-</span>
|
|
1954
|
+
{% endif %}
|
|
1955
|
+
</td>
|
|
1956
|
+
</tr>
|
|
1957
|
+
{% if (test_result.fail|default(0) > 0 or test_result.error|default(0) > 0) and property_name in property_error_details %}
|
|
1958
|
+
{% set error_list = property_error_details[property_name] %}
|
|
1959
|
+
{% set property_index = loop.index %}
|
|
1960
|
+
{% if error_list|length == 1 %}
|
|
1961
|
+
<!-- Single error detail row -->
|
|
1962
|
+
<tr class="collapse property-detail-row" data-detail-for="{{ property_name }}" id="single-error-detail-{{ property_index }}">
|
|
1963
|
+
<td colspan="8">
|
|
1964
|
+
<div class="bg-light p-3 rounded">
|
|
1965
|
+
<div class="mb-2">
|
|
1966
|
+
<span class="badge bg-{{ 'danger' if error_list[0].state == 'fail' else 'warning' }}">
|
|
1967
|
+
{{ error_list[0].state|upper }}
|
|
1968
|
+
</span>
|
|
1969
|
+
{% if error_list[0].occurrence_count > 1 %}
|
|
1970
|
+
<span class="badge bg-info ms-2">
|
|
1971
|
+
Occurred {{ error_list[0].occurrence_count }} times
|
|
1972
|
+
</span>
|
|
1973
|
+
{% endif %}
|
|
1974
|
+
{% if error_list[0].startStepsCountList is defined and error_list[0].startStepsCountList|length > 0 %}
|
|
1975
|
+
<span class="badge bg-secondary ms-2">
|
|
1976
|
+
<i class="bi bi-step-forward"></i> Monkey Steps: {{ error_list[0].startStepsCountList|join(', ') }}
|
|
1977
|
+
</span>
|
|
1978
|
+
{% endif %}
|
|
1979
|
+
</div>
|
|
1980
|
+
{% if error_list[0].short_description %}
|
|
1981
|
+
<div class="mb-2">
|
|
1982
|
+
<strong>Error:</strong> <code>{{ error_list[0].short_description }}</code>
|
|
1983
|
+
</div>
|
|
1984
|
+
{% endif %}
|
|
1985
|
+
<details>
|
|
1986
|
+
<summary class="btn btn-sm btn-outline-secondary mb-2">Show Full Traceback</summary>
|
|
1987
|
+
<pre class="text-danger mb-0 text-start" style="font-size: 0.85rem; white-space: pre-wrap; text-align: left;">{{ error_list[0].traceback }}</pre>
|
|
1988
|
+
</details>
|
|
1989
|
+
</div>
|
|
1990
|
+
</td>
|
|
1991
|
+
</tr>
|
|
1992
|
+
{% else %}
|
|
1993
|
+
<!-- Multiple errors detail row -->
|
|
1994
|
+
<tr class="collapse property-detail-row" data-detail-for="{{ property_name }}" id="multi-error-detail-{{ property_index }}">
|
|
1995
|
+
<td colspan="8">
|
|
1996
|
+
<div class="bg-light p-3 rounded">
|
|
1997
|
+
<!-- Error summary -->
|
|
1998
|
+
<div class="mb-3">
|
|
1999
|
+
<h6 class="text-danger">Multiple Errors Detected</h6>
|
|
2000
|
+
<div class="d-flex flex-wrap gap-1">
|
|
2001
|
+
{% for error in error_list %}
|
|
2002
|
+
<span class="badge bg-{{ 'danger' if error.state == 'fail' else 'warning' }}">
|
|
2003
|
+
{{ error.state|upper }} #{{ loop.index }}
|
|
2004
|
+
{% if error.occurrence_count > 1 %} ({{ error.occurrence_count }}x){% endif %}
|
|
2005
|
+
{% if error.startStepsCountList is defined and error.startStepsCountList|length > 0 %}
|
|
2006
|
+
@{% if error.startStepsCountList|length == 1 %}{{ error.startStepsCountList[0] }}{% else %}{{ error.startStepsCountList[0] }}-{{ error.startStepsCountList[-1] }}{% endif %}
|
|
2007
|
+
{% endif %}
|
|
2008
|
+
</span>
|
|
2009
|
+
{% endfor %}
|
|
2010
|
+
</div>
|
|
2011
|
+
</div>
|
|
2012
|
+
|
|
2013
|
+
<!-- Error tabs -->
|
|
2014
|
+
<ul class="nav nav-pills nav-fill mb-3" id="multi-error-tabs-{{ property_index }}" role="tablist">
|
|
2015
|
+
{% for error in error_list %}
|
|
2016
|
+
<li class="nav-item" role="presentation">
|
|
2017
|
+
<button class="nav-link {{ 'active' if loop.first else '' }} btn-sm"
|
|
2018
|
+
id="multi-error-tab-{{ property_index }}-{{ loop.index }}"
|
|
2019
|
+
data-bs-toggle="pill"
|
|
2020
|
+
data-bs-target="#multi-error-content-{{ property_index }}-{{ loop.index }}"
|
|
2021
|
+
type="button" role="tab"
|
|
2022
|
+
aria-controls="multi-error-content-{{ property_index }}-{{ loop.index }}"
|
|
2023
|
+
aria-selected="{{ 'true' if loop.first else 'false' }}">
|
|
2024
|
+
<span class="badge bg-{{ 'danger' if error.state == 'fail' else 'warning' }} me-1">
|
|
2025
|
+
{{ error.state|upper }}
|
|
2026
|
+
</span>
|
|
2027
|
+
#{{ loop.index }}
|
|
2028
|
+
</button>
|
|
2029
|
+
</li>
|
|
2030
|
+
{% endfor %}
|
|
2031
|
+
</ul>
|
|
2032
|
+
|
|
2033
|
+
<!-- Error content -->
|
|
2034
|
+
<div class="tab-content" id="multi-error-content-{{ property_index }}">
|
|
2035
|
+
{% for error in error_list %}
|
|
2036
|
+
<div class="tab-pane fade {{ 'show active' if loop.first else '' }}"
|
|
2037
|
+
id="multi-error-content-{{ property_index }}-{{ loop.index }}"
|
|
2038
|
+
role="tabpanel"
|
|
2039
|
+
aria-labelledby="multi-error-tab-{{ property_index }}-{{ loop.index }}">
|
|
2040
|
+
<div class="mb-2">
|
|
2041
|
+
<span class="badge bg-{{ 'danger' if error.state == 'fail' else 'warning' }}">
|
|
2042
|
+
{{ error.state|upper }} #{{ loop.index }}
|
|
2043
|
+
</span>
|
|
2044
|
+
<small class="text-muted ms-2">Error {{ loop.index }} of {{ loop.length }}</small>
|
|
2045
|
+
{% if error.occurrence_count > 1 %}
|
|
2046
|
+
<span class="badge bg-info ms-2">
|
|
2047
|
+
{{ error.occurrence_count }} occurrences
|
|
2048
|
+
</span>
|
|
2049
|
+
{% endif %}
|
|
2050
|
+
{% if error.startStepsCountList is defined and error.startStepsCountList|length > 0 %}
|
|
2051
|
+
<span class="badge bg-secondary ms-2">
|
|
2052
|
+
<i class="bi bi-step-forward"></i> Monkey Steps: {{ error.startStepsCountList|join(', ') }}
|
|
2053
|
+
</span>
|
|
2054
|
+
{% endif %}
|
|
2055
|
+
</div>
|
|
2056
|
+
{% if error.short_description %}
|
|
2057
|
+
<div class="mb-2">
|
|
2058
|
+
<strong>Error:</strong> <code>{{ error.short_description }}</code>
|
|
2059
|
+
</div>
|
|
2060
|
+
{% endif %}
|
|
2061
|
+
<details>
|
|
2062
|
+
<summary class="btn btn-sm btn-outline-secondary mb-2">Show Full Traceback</summary>
|
|
2063
|
+
<pre class="text-danger mb-0 text-start" style="font-size: 0.85rem; white-space: pre-wrap; text-align: left;">{{ error.traceback }}</pre>
|
|
2064
|
+
</details>
|
|
2065
|
+
</div>
|
|
2066
|
+
{% endfor %}
|
|
2067
|
+
</div>
|
|
2068
|
+
</div>
|
|
2069
|
+
</td>
|
|
2070
|
+
</tr>
|
|
2071
|
+
{% endif %}
|
|
2072
|
+
{% endif %}
|
|
2073
|
+
{% endfor %}
|
|
2074
|
+
</tbody>
|
|
2075
|
+
</table>
|
|
2076
|
+
|
|
2077
|
+
<!-- Pagination for Property Checking Statistics -->
|
|
2078
|
+
<div class="d-flex justify-content-between align-items-center mt-3">
|
|
2079
|
+
<div class="d-flex align-items-center">
|
|
2080
|
+
<label for="stats-page-size" class="form-label me-2 mb-0">Show:</label>
|
|
2081
|
+
<select class="form-select form-select-sm" id="stats-page-size" style="width: auto;">
|
|
2082
|
+
<option value="5">5</option>
|
|
2083
|
+
<option value="10" selected>10</option>
|
|
2084
|
+
<option value="20">20</option>
|
|
2085
|
+
<option value="50">50</option>
|
|
2086
|
+
<option value="100">100</option>
|
|
2087
|
+
</select>
|
|
2088
|
+
</div>
|
|
2089
|
+
<nav aria-label="Property Stats Pagination">
|
|
2090
|
+
<ul class="pagination pagination-sm mb-0" id="stats-pagination">
|
|
2091
|
+
<!-- Pagination will be generated by JavaScript -->
|
|
2092
|
+
</ul>
|
|
2093
|
+
</nav>
|
|
2094
|
+
</div>
|
|
2095
|
+
</div>
|
|
2096
|
+
</div>
|
|
2097
|
+
</div>
|
|
2098
|
+
|
|
2099
|
+
<!-- Footer -->
|
|
2100
|
+
<footer class="bg-dark text-white text-center py-4">
|
|
2101
|
+
<div class="container">
|
|
2102
|
+
<p class="mb-0">Kea2 Test Report | Generated at: {{ timestamp }}</p>
|
|
2103
|
+
</div>
|
|
2104
|
+
</footer>
|
|
2105
|
+
|
|
2106
|
+
<!-- JavaScript -->
|
|
2107
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
2108
|
+
<script>
|
|
2109
|
+
// Draw coverage trend chart
|
|
2110
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
2111
|
+
var coverageData = {{ coverage_data|safe }};
|
|
2112
|
+
console.log("Coverage data points:", coverageData.length);
|
|
2113
|
+
|
|
2114
|
+
coverageData.sort(function(a, b) {
|
|
2115
|
+
return a.stepsCount - b.stepsCount;
|
|
2116
|
+
});
|
|
2117
|
+
|
|
2118
|
+
var steps = coverageData.map(function(item) { return item.stepsCount; });
|
|
2119
|
+
var coverages = coverageData.map(function(item) { return item.coverage; });
|
|
2120
|
+
var testedActivities = coverageData.map(function(item) { return item.testedActivitiesCount; });
|
|
2121
|
+
|
|
2122
|
+
if (steps.length > 0 && steps[0] > 0) {
|
|
2123
|
+
steps.unshift(0);
|
|
2124
|
+
coverages.unshift(0);
|
|
2125
|
+
testedActivities.unshift(0);
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
console.log("Steps with zero point:", steps);
|
|
2129
|
+
console.log("Coverage values with zero point:", coverages);
|
|
2130
|
+
|
|
2131
|
+
var ctx = document.getElementById('coverageChart').getContext('2d');
|
|
2132
|
+
var chart = new Chart(ctx, {
|
|
2133
|
+
type: 'line',
|
|
2134
|
+
data: {
|
|
2135
|
+
labels: steps,
|
|
2136
|
+
datasets: [
|
|
2137
|
+
{
|
|
2138
|
+
label: 'Activity Coverage (%)',
|
|
2139
|
+
data: coverages.map((value, index) => ({x: steps[index], y: value})),
|
|
2140
|
+
borderColor: '#3498db',
|
|
2141
|
+
backgroundColor: 'rgba(52, 152, 219, 0.1)',
|
|
2142
|
+
borderWidth: 3,
|
|
2143
|
+
fill: true,
|
|
2144
|
+
tension: 0.4,
|
|
2145
|
+
yAxisID: 'y',
|
|
2146
|
+
pointRadius: 4,
|
|
2147
|
+
pointHoverRadius: 6
|
|
2148
|
+
},
|
|
2149
|
+
{
|
|
2150
|
+
label: 'Tested Activities',
|
|
2151
|
+
data: testedActivities.map((value, index) => ({x: steps[index], y: value})),
|
|
2152
|
+
borderColor: '#2ecc71',
|
|
2153
|
+
backgroundColor: 'rgba(46, 204, 113, 0.1)',
|
|
2154
|
+
borderWidth: 3,
|
|
2155
|
+
fill: true,
|
|
2156
|
+
tension: 0.4,
|
|
2157
|
+
yAxisID: 'y1',
|
|
2158
|
+
pointRadius: 4,
|
|
2159
|
+
pointHoverRadius: 6
|
|
2160
|
+
}
|
|
2161
|
+
]
|
|
2162
|
+
},
|
|
2163
|
+
options: {
|
|
2164
|
+
responsive: true,
|
|
2165
|
+
maintainAspectRatio: false,
|
|
2166
|
+
aspectRatio: 2,
|
|
2167
|
+
plugins: {
|
|
2168
|
+
legend: {
|
|
2169
|
+
position: 'top',
|
|
2170
|
+
labels: {
|
|
2171
|
+
boxWidth: 15,
|
|
2172
|
+
usePointStyle: true,
|
|
2173
|
+
pointStyle: 'circle'
|
|
2174
|
+
}
|
|
2175
|
+
},
|
|
2176
|
+
tooltip: {
|
|
2177
|
+
mode: 'index',
|
|
2178
|
+
intersect: false,
|
|
2179
|
+
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
|
2180
|
+
titleColor: '#fff',
|
|
2181
|
+
bodyColor: '#fff',
|
|
2182
|
+
borderColor: 'rgba(255, 255, 255, 0.2)',
|
|
2183
|
+
borderWidth: 1,
|
|
2184
|
+
padding: 10,
|
|
2185
|
+
displayColors: true,
|
|
2186
|
+
callbacks: {
|
|
2187
|
+
label: function(context) {
|
|
2188
|
+
var label = context.dataset.label || '';
|
|
2189
|
+
if (label) {
|
|
2190
|
+
label += ': ';
|
|
2191
|
+
}
|
|
2192
|
+
if (context.datasetIndex === 0) {
|
|
2193
|
+
label += context.parsed.y.toFixed(2) + '%';
|
|
2194
|
+
} else {
|
|
2195
|
+
label += context.parsed.y;
|
|
2196
|
+
}
|
|
2197
|
+
return label;
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
},
|
|
2202
|
+
scales: {
|
|
2203
|
+
x: {
|
|
2204
|
+
type: 'linear',
|
|
2205
|
+
beginAtZero: true,
|
|
2206
|
+
max: steps.length > 0 ? Math.max(...steps) : 10,
|
|
2207
|
+
grid: {
|
|
2208
|
+
display: false
|
|
2209
|
+
},
|
|
2210
|
+
title: {
|
|
2211
|
+
display: true,
|
|
2212
|
+
text: 'Steps Count',
|
|
2213
|
+
font: {
|
|
2214
|
+
size: 14
|
|
2215
|
+
}
|
|
2216
|
+
},
|
|
2217
|
+
ticks: {
|
|
2218
|
+
stepSize: steps.length > 0 ? Math.max(1, Math.ceil(Math.max(...steps) / 10)) : 1,
|
|
2219
|
+
callback: function(value) {
|
|
2220
|
+
if (Number.isInteger(value) && value <= (steps.length > 0 ? Math.max(...steps) : 10)) {
|
|
2221
|
+
return value;
|
|
2222
|
+
}
|
|
2223
|
+
return null;
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
},
|
|
2227
|
+
y: {
|
|
2228
|
+
beginAtZero: true,
|
|
2229
|
+
title: {
|
|
2230
|
+
display: true,
|
|
2231
|
+
text: 'Activity Coverage (%)',
|
|
2232
|
+
font: {
|
|
2233
|
+
size: 14
|
|
2234
|
+
}
|
|
2235
|
+
},
|
|
2236
|
+
grid: {
|
|
2237
|
+
borderDash: [5, 5]
|
|
2238
|
+
}
|
|
2239
|
+
},
|
|
2240
|
+
y1: {
|
|
2241
|
+
position: 'right',
|
|
2242
|
+
beginAtZero: true,
|
|
2243
|
+
title: {
|
|
2244
|
+
display: true,
|
|
2245
|
+
text: 'Tested Activities',
|
|
2246
|
+
font: {
|
|
2247
|
+
size: 14
|
|
2248
|
+
}
|
|
2249
|
+
},
|
|
2250
|
+
grid: {
|
|
2251
|
+
display: false
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
},
|
|
2255
|
+
interaction: {
|
|
2256
|
+
mode: 'index',
|
|
2257
|
+
intersect: false
|
|
2258
|
+
},
|
|
2259
|
+
hover: {
|
|
2260
|
+
mode: 'index',
|
|
2261
|
+
intersect: false
|
|
2262
|
+
},
|
|
2263
|
+
animation: {
|
|
2264
|
+
duration: 1000,
|
|
2265
|
+
easing: 'easeOutQuart'
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
});
|
|
2269
|
+
|
|
2270
|
+
// Draw property execution trend chart
|
|
2271
|
+
var propertyExecutionData = {{ property_execution_data|safe }};
|
|
2272
|
+
console.log("Property execution data points:", propertyExecutionData.length);
|
|
2273
|
+
|
|
2274
|
+
// Ensure we have valid data
|
|
2275
|
+
if (propertyExecutionData.length === 0) {
|
|
2276
|
+
propertyExecutionData = [{"stepsCount": 0, "executedPropertiesCount": 0}];
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
propertyExecutionData.sort(function(a, b) {
|
|
2280
|
+
return a.stepsCount - b.stepsCount;
|
|
2281
|
+
});
|
|
2282
|
+
|
|
2283
|
+
var propSteps = propertyExecutionData.map(function(item) { return item.stepsCount; });
|
|
2284
|
+
var executedProps = propertyExecutionData.map(function(item) { return item.executedPropertiesCount; });
|
|
2285
|
+
|
|
2286
|
+
// Add zero starting point if needed
|
|
2287
|
+
if (propSteps.length > 0 && propSteps[0] > 0) {
|
|
2288
|
+
propSteps.unshift(0);
|
|
2289
|
+
executedProps.unshift(0);
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
console.log("Property execution steps:", propSteps);
|
|
2293
|
+
console.log("Executed properties values:", executedProps);
|
|
2294
|
+
|
|
2295
|
+
var propCtx = document.getElementById('propertyExecutionChart').getContext('2d');
|
|
2296
|
+
var propChart = new Chart(propCtx, {
|
|
2297
|
+
type: 'line',
|
|
2298
|
+
data: {
|
|
2299
|
+
labels: propSteps,
|
|
2300
|
+
datasets: [
|
|
2301
|
+
{
|
|
2302
|
+
label: 'Executed Properties',
|
|
2303
|
+
data: executedProps.map((value, index) => ({x: propSteps[index], y: value})),
|
|
2304
|
+
borderColor: '#e74c3c',
|
|
2305
|
+
backgroundColor: 'rgba(231, 76, 60, 0.1)',
|
|
2306
|
+
borderWidth: 3,
|
|
2307
|
+
fill: true,
|
|
2308
|
+
tension: 0.4,
|
|
2309
|
+
pointRadius: 4,
|
|
2310
|
+
pointHoverRadius: 6
|
|
2311
|
+
}
|
|
2312
|
+
]
|
|
2313
|
+
},
|
|
2314
|
+
options: {
|
|
2315
|
+
responsive: true,
|
|
2316
|
+
maintainAspectRatio: false,
|
|
2317
|
+
aspectRatio: 2,
|
|
2318
|
+
plugins: {
|
|
2319
|
+
legend: {
|
|
2320
|
+
position: 'top',
|
|
2321
|
+
labels: {
|
|
2322
|
+
boxWidth: 15,
|
|
2323
|
+
usePointStyle: true,
|
|
2324
|
+
pointStyle: 'circle'
|
|
2325
|
+
}
|
|
2326
|
+
},
|
|
2327
|
+
tooltip: {
|
|
2328
|
+
mode: 'index',
|
|
2329
|
+
intersect: false,
|
|
2330
|
+
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
|
2331
|
+
titleColor: '#fff',
|
|
2332
|
+
bodyColor: '#fff',
|
|
2333
|
+
borderColor: 'rgba(255, 255, 255, 0.2)',
|
|
2334
|
+
borderWidth: 1,
|
|
2335
|
+
padding: 10,
|
|
2336
|
+
displayColors: true,
|
|
2337
|
+
callbacks: {
|
|
2338
|
+
label: function(context) {
|
|
2339
|
+
return 'Executed Properties: ' + context.parsed.y;
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
},
|
|
2344
|
+
scales: {
|
|
2345
|
+
x: {
|
|
2346
|
+
type: 'linear',
|
|
2347
|
+
beginAtZero: true,
|
|
2348
|
+
max: propSteps.length > 0 ? Math.max(...propSteps) : 10,
|
|
2349
|
+
grid: {
|
|
2350
|
+
display: false
|
|
2351
|
+
},
|
|
2352
|
+
title: {
|
|
2353
|
+
display: true,
|
|
2354
|
+
text: 'Steps Count',
|
|
2355
|
+
font: {
|
|
2356
|
+
size: 14
|
|
2357
|
+
}
|
|
2358
|
+
},
|
|
2359
|
+
ticks: {
|
|
2360
|
+
stepSize: propSteps.length > 0 ? Math.max(1, Math.ceil(Math.max(...propSteps) / 10)) : 1,
|
|
2361
|
+
callback: function(value) {
|
|
2362
|
+
if (Number.isInteger(value) && value <= (propSteps.length > 0 ? Math.max(...propSteps) : 10)) {
|
|
2363
|
+
return value;
|
|
2364
|
+
}
|
|
2365
|
+
return null;
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
},
|
|
2369
|
+
y: {
|
|
2370
|
+
beginAtZero: true,
|
|
2371
|
+
title: {
|
|
2372
|
+
display: true,
|
|
2373
|
+
text: 'Executed Properties',
|
|
2374
|
+
font: {
|
|
2375
|
+
size: 14
|
|
2376
|
+
}
|
|
2377
|
+
},
|
|
2378
|
+
grid: {
|
|
2379
|
+
borderDash: [5, 5]
|
|
2380
|
+
},
|
|
2381
|
+
ticks: {
|
|
2382
|
+
stepSize: 1,
|
|
2383
|
+
callback: function(value) {
|
|
2384
|
+
if (Number.isInteger(value)) {
|
|
2385
|
+
return value;
|
|
2386
|
+
}
|
|
2387
|
+
return null;
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
},
|
|
2392
|
+
interaction: {
|
|
2393
|
+
mode: 'index',
|
|
2394
|
+
intersect: false
|
|
2395
|
+
},
|
|
2396
|
+
hover: {
|
|
2397
|
+
mode: 'index',
|
|
2398
|
+
intersect: false
|
|
2399
|
+
},
|
|
2400
|
+
animation: {
|
|
2401
|
+
duration: 1000,
|
|
2402
|
+
easing: 'easeOutQuart'
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
});
|
|
2406
|
+
|
|
2407
|
+
// Draw crash timeline chart
|
|
2408
|
+
var crashTimelineData = {{ crash_timeline_data|default('{}')|safe }};
|
|
2409
|
+
if (crashTimelineData && Object.keys(crashTimelineData).length > 0) {
|
|
2410
|
+
drawCrashTimelineChart(crashTimelineData);
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2413
|
+
// Initialize crash events functionality
|
|
2414
|
+
initCrashAnalysis();
|
|
2415
|
+
|
|
2416
|
+
// Initialize pagination for Activities table
|
|
2417
|
+
initPagination('activities-container', 'activity-row', 'activities-pagination', 'activities-page-size');
|
|
2418
|
+
|
|
2419
|
+
// Initialize activity sorting
|
|
2420
|
+
initActivitiesSorting();
|
|
2421
|
+
|
|
2422
|
+
// Initialize activity searching
|
|
2423
|
+
initActivitiesSearching();
|
|
2424
|
+
|
|
2425
|
+
// Initialize activities page size selector
|
|
2426
|
+
initActivitiesPageSize();
|
|
2427
|
+
|
|
2428
|
+
// Initialize property statistics searching
|
|
2429
|
+
initPropertySearching();
|
|
2430
|
+
initPropertyKindFilter();
|
|
2431
|
+
|
|
2432
|
+
// Initialize pagination for Property tables
|
|
2433
|
+
initPagination('property-violations-container', 'property-violation-row', 'violations-pagination', 'violations-page-size');
|
|
2434
|
+
initViolationFilter();
|
|
2435
|
+
initPagination('property-stats-container', 'property-stat-row', 'stats-pagination', 'stats-page-size');
|
|
2436
|
+
|
|
2437
|
+
// Initialize sorting for Property Checking Statistics
|
|
2438
|
+
initSorting();
|
|
2439
|
+
|
|
2440
|
+
// Activity sorting function
|
|
2441
|
+
function initActivitySorting() {
|
|
2442
|
+
const sortButtons = document.querySelectorAll('.activity-sort-btn');
|
|
2443
|
+
|
|
2444
|
+
sortButtons.forEach(function(button) {
|
|
2445
|
+
button.addEventListener('click', function() {
|
|
2446
|
+
const sortType = this.dataset.sort;
|
|
2447
|
+
const currentOrder = this.dataset.order;
|
|
2448
|
+
const parentTab = this.closest('.tab-pane');
|
|
2449
|
+
const isTestedTab = parentTab.id === 'tested-activities';
|
|
2450
|
+
|
|
2451
|
+
// Toggle sort order
|
|
2452
|
+
const newOrder = currentOrder === 'asc' ? 'desc' : 'asc';
|
|
2453
|
+
|
|
2454
|
+
this.dataset.order = newOrder;
|
|
2455
|
+
const icon = this.querySelector('.sort-icon');
|
|
2456
|
+
|
|
2457
|
+
if (newOrder === 'asc') {
|
|
2458
|
+
icon.className = 'bi bi-arrow-up sort-icon btn-arrow';
|
|
2459
|
+
} else {
|
|
2460
|
+
icon.className = 'bi bi-arrow-down sort-icon btn-arrow';
|
|
2461
|
+
}
|
|
2462
|
+
|
|
2463
|
+
// Sort activities
|
|
2464
|
+
sortActivities(sortType, newOrder, isTestedTab);
|
|
2465
|
+
});
|
|
2466
|
+
});
|
|
2467
|
+
|
|
2468
|
+
function sortActivities(sortType, order, isTestedTab) {
|
|
2469
|
+
const containerId = isTestedTab ? 'tested-activities-container' : 'all-activities-container';
|
|
2470
|
+
const itemClass = isTestedTab ? 'tested-activity' : 'all-activity';
|
|
2471
|
+
const paginationId = isTestedTab ? 'tested-pagination' : 'all-pagination';
|
|
2472
|
+
const pageSizeSelectId = isTestedTab ? 'tested-page-size' : 'all-page-size';
|
|
2473
|
+
|
|
2474
|
+
const container = document.getElementById(containerId);
|
|
2475
|
+
const items = Array.from(container.getElementsByClassName(itemClass));
|
|
2476
|
+
|
|
2477
|
+
items.sort(function(a, b) {
|
|
2478
|
+
const badgeA = a.querySelector('.traversal-badge');
|
|
2479
|
+
const badgeB = b.querySelector('.traversal-badge');
|
|
2480
|
+
|
|
2481
|
+
// Extract traversal count from badge text like "👁 5 times"
|
|
2482
|
+
const valueA = badgeA ? parseInt(badgeA.textContent.match(/\d+/)[0]) || 0 : 0;
|
|
2483
|
+
const valueB = badgeB ? parseInt(badgeB.textContent.match(/\d+/)[0]) || 0 : 0;
|
|
2484
|
+
|
|
2485
|
+
if (order === 'asc') {
|
|
2486
|
+
return valueA - valueB;
|
|
2487
|
+
} else {
|
|
2488
|
+
return valueB - valueA;
|
|
2489
|
+
}
|
|
2490
|
+
});
|
|
2491
|
+
|
|
2492
|
+
// Clear container and append sorted items
|
|
2493
|
+
container.innerHTML = '';
|
|
2494
|
+
items.forEach(function(item) {
|
|
2495
|
+
container.appendChild(item);
|
|
2496
|
+
});
|
|
2497
|
+
|
|
2498
|
+
// Check if there's an active search and reapply it
|
|
2499
|
+
const searchInputId = isTestedTab ? 'tested-activity-search' : 'all-activity-search';
|
|
2500
|
+
const searchInput = document.getElementById(searchInputId);
|
|
2501
|
+
if (searchInput && searchInput.value.trim() !== '') {
|
|
2502
|
+
// Reapply search filter after sorting
|
|
2503
|
+
performActivitySearch(searchInput);
|
|
2504
|
+
} else {
|
|
2505
|
+
// No active search, just re-initialize pagination
|
|
2506
|
+
initPagination(containerId, itemClass, paginationId, pageSizeSelectId);
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
|
|
2511
|
+
// Simplified sorting function for Fails and Errors columns (Property Checking Statistics only)
|
|
2512
|
+
function initSorting() {
|
|
2513
|
+
// Only select sort icons that are NOT activities sort icons
|
|
2514
|
+
const sortIcons = document.querySelectorAll('.sort-icon:not(.activities-sort-icon)');
|
|
2515
|
+
|
|
2516
|
+
sortIcons.forEach(function(icon) {
|
|
2517
|
+
icon.addEventListener('click', function() {
|
|
2518
|
+
const column = this.dataset.column;
|
|
2519
|
+
const currentOrder = this.dataset.order;
|
|
2520
|
+
|
|
2521
|
+
// Reset all other property sort icons (not activities)
|
|
2522
|
+
sortIcons.forEach(function(otherIcon) {
|
|
2523
|
+
if (otherIcon !== icon) {
|
|
2524
|
+
otherIcon.dataset.order = 'none';
|
|
2525
|
+
otherIcon.className = 'bi bi-arrow-down-up sort-icon';
|
|
2526
|
+
otherIcon.style.color = '#6c757d';
|
|
2527
|
+
}
|
|
2528
|
+
});
|
|
2529
|
+
|
|
2530
|
+
// Toggle current sort order
|
|
2531
|
+
let newOrder;
|
|
2532
|
+
if (currentOrder === 'none' || currentOrder === 'desc') {
|
|
2533
|
+
newOrder = 'asc';
|
|
2534
|
+
this.className = 'bi bi-arrow-up sort-icon active asc';
|
|
2535
|
+
this.style.color = '#28a745';
|
|
2536
|
+
} else {
|
|
2537
|
+
newOrder = 'desc';
|
|
2538
|
+
this.className = 'bi bi-arrow-down sort-icon active desc';
|
|
2539
|
+
this.style.color = '#dc3545';
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2542
|
+
this.dataset.order = newOrder;
|
|
2543
|
+
sortTable(column, newOrder);
|
|
2544
|
+
});
|
|
2545
|
+
});
|
|
2546
|
+
|
|
2547
|
+
function sortTable(column, order) {
|
|
2548
|
+
const container = document.getElementById('property-stats-container');
|
|
2549
|
+
const rows = Array.from(container.getElementsByClassName('property-stat-row'));
|
|
2550
|
+
const detailRowMap = new Map();
|
|
2551
|
+
|
|
2552
|
+
// Capture current detail rows so they can move with their parent property row
|
|
2553
|
+
const detailRows = Array.from(container.querySelectorAll('.property-detail-row'));
|
|
2554
|
+
detailRows.forEach(function(detailRow) {
|
|
2555
|
+
const key = detailRow.dataset.detailFor;
|
|
2556
|
+
if (!key) return;
|
|
2557
|
+
if (!detailRowMap.has(key)) {
|
|
2558
|
+
detailRowMap.set(key, []);
|
|
2559
|
+
}
|
|
2560
|
+
detailRowMap.get(key).push(detailRow);
|
|
2561
|
+
});
|
|
2562
|
+
|
|
2563
|
+
rows.sort(function(a, b) {
|
|
2564
|
+
let valueA, valueB;
|
|
2565
|
+
|
|
2566
|
+
if (column === 'fails') {
|
|
2567
|
+
valueA = parseInt(a.dataset.fails);
|
|
2568
|
+
valueB = parseInt(b.dataset.fails);
|
|
2569
|
+
} else if (column === 'errors') {
|
|
2570
|
+
valueA = parseInt(a.dataset.errors);
|
|
2571
|
+
valueB = parseInt(b.dataset.errors);
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2574
|
+
if (order === 'asc') {
|
|
2575
|
+
return valueA - valueB;
|
|
2576
|
+
} else {
|
|
2577
|
+
return valueB - valueA;
|
|
2578
|
+
}
|
|
2579
|
+
});
|
|
2580
|
+
|
|
2581
|
+
const fragment = document.createDocumentFragment();
|
|
2582
|
+
rows.forEach(function(row) {
|
|
2583
|
+
fragment.appendChild(row);
|
|
2584
|
+
const detailKey = row.dataset.propertyName;
|
|
2585
|
+
const relatedDetailRows = detailRowMap.get(detailKey) || [];
|
|
2586
|
+
relatedDetailRows.forEach(function(detailRow) {
|
|
2587
|
+
fragment.appendChild(detailRow);
|
|
2588
|
+
});
|
|
2589
|
+
});
|
|
2590
|
+
|
|
2591
|
+
container.innerHTML = '';
|
|
2592
|
+
container.appendChild(fragment);
|
|
2593
|
+
|
|
2594
|
+
// Re-initialize pagination after sorting
|
|
2595
|
+
initPagination('property-stats-container', 'property-stat-row', 'stats-pagination', 'stats-page-size');
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
|
|
2599
|
+
// Function to handle page navigation
|
|
2600
|
+
function goToPage(pageNumber, containerId, itemClass, itemsPerPage, paginationId) {
|
|
2601
|
+
const container = document.getElementById(containerId);
|
|
2602
|
+
const allItems = Array.from(container.getElementsByClassName(itemClass));
|
|
2603
|
+
const paginationElement = document.getElementById(paginationId);
|
|
2604
|
+
|
|
2605
|
+
if (containerId === 'property-stats-container') {
|
|
2606
|
+
closePropertyErrorDetails(container);
|
|
2607
|
+
}
|
|
2608
|
+
|
|
2609
|
+
console.log('goToPage called with:', {pageNumber, containerId, itemClass, itemsPerPage, paginationId});
|
|
2610
|
+
console.log('Total items found:', allItems.length);
|
|
2611
|
+
|
|
2612
|
+
// Get items that should be visible based on search filter
|
|
2613
|
+
const filteredItems = allItems.filter(item => {
|
|
2614
|
+
const searchVisible = item.getAttribute('data-search-visible');
|
|
2615
|
+
const kindVisible = item.getAttribute('data-kind-visible');
|
|
2616
|
+
const shouldShow = (searchVisible === null || searchVisible === 'true') &&
|
|
2617
|
+
(kindVisible === null || kindVisible === 'true');
|
|
2618
|
+
return shouldShow;
|
|
2619
|
+
});
|
|
2620
|
+
|
|
2621
|
+
console.log('Filtered items count:', filteredItems.length);
|
|
2622
|
+
console.log('Items with search-visible=true:', allItems.filter(item => item.getAttribute('data-search-visible') === 'true').length);
|
|
2623
|
+
console.log('Items with search-visible=false:', allItems.filter(item => item.getAttribute('data-search-visible') === 'false').length);
|
|
2624
|
+
console.log('Items with search-visible=null:', allItems.filter(item => item.getAttribute('data-search-visible') === null).length);
|
|
2625
|
+
|
|
2626
|
+
// Update pagination active state
|
|
2627
|
+
if (paginationElement) {
|
|
2628
|
+
const pageItems = paginationElement.getElementsByClassName('page-item');
|
|
2629
|
+
for (let i = 0; i < pageItems.length; i++) {
|
|
2630
|
+
if (pageItems[i].textContent.trim() === pageNumber.toString()) {
|
|
2631
|
+
pageItems[i].className = 'page-item active';
|
|
2632
|
+
} else if (pageItems[i].textContent.trim() !== '«' && pageItems[i].textContent.trim() !== '»') {
|
|
2633
|
+
pageItems[i].className = 'page-item';
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
|
|
2638
|
+
// Hide all items first
|
|
2639
|
+
console.log('Hiding all items...');
|
|
2640
|
+
allItems.forEach((item, index) => {
|
|
2641
|
+
item.style.display = 'none';
|
|
2642
|
+
});
|
|
2643
|
+
|
|
2644
|
+
// Show items for current page (only from filtered items)
|
|
2645
|
+
const startIndex = (pageNumber - 1) * itemsPerPage;
|
|
2646
|
+
const endIndex = Math.min(startIndex + itemsPerPage, filteredItems.length);
|
|
2647
|
+
|
|
2648
|
+
console.log('Showing items from index', startIndex, 'to', endIndex - 1);
|
|
2649
|
+
|
|
2650
|
+
for (let i = startIndex; i < endIndex; i++) {
|
|
2651
|
+
if (filteredItems[i]) {
|
|
2652
|
+
filteredItems[i].style.display = '';
|
|
2653
|
+
console.log('Showing item', i, ':', filteredItems[i].querySelector('.activity-name')?.textContent);
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
console.log('goToPage completed');
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2660
|
+
// Pagination function
|
|
2661
|
+
function initPagination(containerId, itemClass, paginationId, pageSizeSelectId) {
|
|
2662
|
+
const container = document.getElementById(containerId);
|
|
2663
|
+
const pageSizeSelect = document.getElementById(pageSizeSelectId);
|
|
2664
|
+
if (!container) return;
|
|
2665
|
+
|
|
2666
|
+
// Remove existing event listener to prevent duplicate bindings
|
|
2667
|
+
if (pageSizeSelect && !pageSizeSelect.hasAttribute('data-listener-bound')) {
|
|
2668
|
+
pageSizeSelect.addEventListener('change', function() {
|
|
2669
|
+
const newItemsPerPage = parseInt(this.value);
|
|
2670
|
+
renderPagination();
|
|
2671
|
+
goToPage(1, containerId, itemClass, newItemsPerPage, paginationId);
|
|
2672
|
+
});
|
|
2673
|
+
// Mark as having listener bound
|
|
2674
|
+
pageSizeSelect.setAttribute('data-listener-bound', 'true');
|
|
2675
|
+
}
|
|
2676
|
+
|
|
2677
|
+
function renderPagination() {
|
|
2678
|
+
const allItems = Array.from(container.getElementsByClassName(itemClass));
|
|
2679
|
+
|
|
2680
|
+
// Always get current page size from select element
|
|
2681
|
+
const currentPageSizeSelect = document.getElementById(pageSizeSelectId);
|
|
2682
|
+
const currentItemsPerPage = currentPageSizeSelect ? parseInt(currentPageSizeSelect.value) : 10;
|
|
2683
|
+
|
|
2684
|
+
// Use same filtering logic as goToPage function
|
|
2685
|
+
const filteredItems = allItems.filter(item => {
|
|
2686
|
+
const searchVisible = item.getAttribute('data-search-visible');
|
|
2687
|
+
const kindVisible = item.getAttribute('data-kind-visible');
|
|
2688
|
+
// If no filters are applied, show all items
|
|
2689
|
+
return (searchVisible === null || searchVisible === 'true') &&
|
|
2690
|
+
(kindVisible === null || kindVisible === 'true');
|
|
2691
|
+
});
|
|
2692
|
+
|
|
2693
|
+
const totalItems = filteredItems.length;
|
|
2694
|
+
const totalPages = Math.max(1, Math.ceil(totalItems / currentItemsPerPage));
|
|
2695
|
+
|
|
2696
|
+
// Create pagination
|
|
2697
|
+
const paginationElement = document.getElementById(paginationId);
|
|
2698
|
+
if (!paginationElement) return;
|
|
2699
|
+
|
|
2700
|
+
// Clear pagination
|
|
2701
|
+
paginationElement.innerHTML = '';
|
|
2702
|
+
|
|
2703
|
+
// Don't show pagination if there's only one page or no items
|
|
2704
|
+
if (totalPages <= 1) {
|
|
2705
|
+
// Hide the entire pagination container when not needed
|
|
2706
|
+
const paginationContainer = paginationElement.closest('.pagination-container');
|
|
2707
|
+
if (paginationContainer) {
|
|
2708
|
+
paginationContainer.style.display = 'none';
|
|
2709
|
+
}
|
|
2710
|
+
return;
|
|
2711
|
+
}
|
|
2712
|
+
|
|
2713
|
+
// Show the pagination container when needed
|
|
2714
|
+
const paginationContainer = paginationElement.closest('.pagination-container');
|
|
2715
|
+
if (paginationContainer) {
|
|
2716
|
+
paginationContainer.style.display = '';
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
// Previous button
|
|
2720
|
+
const prevLi = document.createElement('li');
|
|
2721
|
+
prevLi.className = 'page-item disabled';
|
|
2722
|
+
prevLi.innerHTML = '<a class="page-link" href="#" aria-label="Previous"><span aria-hidden="true">«</span></a>';
|
|
2723
|
+
paginationElement.appendChild(prevLi);
|
|
2724
|
+
|
|
2725
|
+
// Add page numbers
|
|
2726
|
+
for (let i = 1; i <= totalPages; i++) {
|
|
2727
|
+
const pageLi = document.createElement('li');
|
|
2728
|
+
pageLi.className = i === 1 ? 'page-item active' : 'page-item';
|
|
2729
|
+
pageLi.innerHTML = `<a class="page-link" href="#">${i}</a>`;
|
|
2730
|
+
pageLi.addEventListener('click', function(e) {
|
|
2731
|
+
e.preventDefault();
|
|
2732
|
+
goToPage(i, containerId, itemClass, currentItemsPerPage, paginationId);
|
|
2733
|
+
});
|
|
2734
|
+
paginationElement.appendChild(pageLi);
|
|
2735
|
+
}
|
|
2736
|
+
|
|
2737
|
+
// Next button
|
|
2738
|
+
const nextLi = document.createElement('li');
|
|
2739
|
+
nextLi.className = totalPages <= 1 ? 'page-item disabled' : 'page-item';
|
|
2740
|
+
nextLi.innerHTML = '<a class="page-link" href="#" aria-label="Next"><span aria-hidden="true">»</span></a>';
|
|
2741
|
+
paginationElement.appendChild(nextLi);
|
|
2742
|
+
|
|
2743
|
+
// Add event listeners to prev/next buttons
|
|
2744
|
+
if (paginationElement.firstChild) {
|
|
2745
|
+
paginationElement.firstChild.addEventListener('click', function(e) {
|
|
2746
|
+
e.preventDefault();
|
|
2747
|
+
const activePage = paginationElement.querySelector('.active');
|
|
2748
|
+
if (activePage && activePage.previousElementSibling && activePage.previousElementSibling.previousElementSibling) {
|
|
2749
|
+
const pageNum = parseInt(activePage.textContent) - 1;
|
|
2750
|
+
if (pageNum >= 1) {
|
|
2751
|
+
goToPage(pageNum, containerId, itemClass, currentItemsPerPage, paginationId);
|
|
2752
|
+
}
|
|
2753
|
+
}
|
|
2754
|
+
});
|
|
2755
|
+
}
|
|
2756
|
+
|
|
2757
|
+
if (paginationElement.lastChild) {
|
|
2758
|
+
paginationElement.lastChild.addEventListener('click', function(e) {
|
|
2759
|
+
e.preventDefault();
|
|
2760
|
+
const activePage = paginationElement.querySelector('.active');
|
|
2761
|
+
if (activePage && activePage.nextElementSibling && activePage.nextElementSibling.nextElementSibling) {
|
|
2762
|
+
const pageNum = parseInt(activePage.textContent) + 1;
|
|
2763
|
+
if (pageNum <= totalPages) {
|
|
2764
|
+
goToPage(pageNum, containerId, itemClass, currentItemsPerPage, paginationId);
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
});
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2770
|
+
|
|
2771
|
+
// Initial render
|
|
2772
|
+
renderPagination();
|
|
2773
|
+
// Always call goToPage to ensure items are displayed correctly, even if no pagination is shown
|
|
2774
|
+
const currentItemsPerPage = pageSizeSelect ? parseInt(pageSizeSelect.value) : 10;
|
|
2775
|
+
goToPage(1, containerId, itemClass, currentItemsPerPage, paginationId);
|
|
2776
|
+
}
|
|
2777
|
+
|
|
2778
|
+
// Scroll functionality
|
|
2779
|
+
const screenshots = document.getElementById('screenshots');
|
|
2780
|
+
if (screenshots) {
|
|
2781
|
+
screenshots.addEventListener('wheel', function(e) {
|
|
2782
|
+
if (e.deltaY !== 0) {
|
|
2783
|
+
e.preventDefault();
|
|
2784
|
+
screenshots.scrollLeft += e.deltaY;
|
|
2785
|
+
}
|
|
2786
|
+
});
|
|
2787
|
+
}
|
|
2788
|
+
|
|
2789
|
+
// Beautify screenshot captions
|
|
2790
|
+
function beautifyScreenshotCaptions() {
|
|
2791
|
+
const captions = document.querySelectorAll('.screenshot-caption');
|
|
2792
|
+
captions.forEach(function(caption) {
|
|
2793
|
+
const originalText = caption.textContent.trim();
|
|
2794
|
+
|
|
2795
|
+
// Handle Monkey Step format: "1. Monkey Step 6: SCROLL_TOP_DOWN"
|
|
2796
|
+
const monkeyStepMatch = originalText.match(/^(\d+)\.\s*Monkey Step (\d+):\s*(.+)$/);
|
|
2797
|
+
if (monkeyStepMatch) {
|
|
2798
|
+
const stepIndex = monkeyStepMatch[1];
|
|
2799
|
+
const monkeyStep = monkeyStepMatch[2];
|
|
2800
|
+
const action = monkeyStepMatch[3];
|
|
2801
|
+
|
|
2802
|
+
caption.innerHTML = `
|
|
2803
|
+
<div class="step-number">${stepIndex}. Monkey Step ${monkeyStep}</div>
|
|
2804
|
+
<div class="step-action">${action}</div>
|
|
2805
|
+
`;
|
|
2806
|
+
return;
|
|
2807
|
+
}
|
|
2808
|
+
|
|
2809
|
+
// Handle Script format: "1. click" or similar
|
|
2810
|
+
const scriptMatch = originalText.match(/^(\d+)\.\s*(.+)$/);
|
|
2811
|
+
if (scriptMatch) {
|
|
2812
|
+
const stepIndex = scriptMatch[1];
|
|
2813
|
+
const action = scriptMatch[2];
|
|
2814
|
+
|
|
2815
|
+
// Check if it's a property info
|
|
2816
|
+
if (action.includes(':')) {
|
|
2817
|
+
const parts = action.split(':');
|
|
2818
|
+
if (parts.length === 2) {
|
|
2819
|
+
caption.innerHTML = `
|
|
2820
|
+
<div class="step-number">${stepIndex}. ${parts[0].trim()}</div>
|
|
2821
|
+
<div class="step-action">${parts[1].trim()}</div>
|
|
2822
|
+
`;
|
|
2823
|
+
return;
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
|
|
2827
|
+
// Simple action
|
|
2828
|
+
caption.innerHTML = `
|
|
2829
|
+
<div class="step-number">${stepIndex}. Script Step</div>
|
|
2830
|
+
<div class="step-action">${action}</div>
|
|
2831
|
+
`;
|
|
2832
|
+
}
|
|
2833
|
+
});
|
|
2834
|
+
}
|
|
2835
|
+
|
|
2836
|
+
// Call beautify function after DOM is ready
|
|
2837
|
+
beautifyScreenshotCaptions();
|
|
2838
|
+
|
|
2839
|
+
// Crash analysis functions
|
|
2840
|
+
function initCrashAnalysis() {
|
|
2841
|
+
// Initialize event filtering
|
|
2842
|
+
var filterButtons = document.querySelectorAll('input[name="event-filter"]');
|
|
2843
|
+
filterButtons.forEach(function(button) {
|
|
2844
|
+
button.addEventListener('change', function() {
|
|
2845
|
+
filterCrashEvents(this.id);
|
|
2846
|
+
});
|
|
2847
|
+
});
|
|
2848
|
+
|
|
2849
|
+
// Initialize copy buttons for stack traces
|
|
2850
|
+
var copyButtons = document.querySelectorAll('.copy-stack-btn');
|
|
2851
|
+
copyButtons.forEach(function(button) {
|
|
2852
|
+
button.addEventListener('click', function() {
|
|
2853
|
+
var stackIndex = this.dataset.stackIndex;
|
|
2854
|
+
copyStackTrace(stackIndex);
|
|
2855
|
+
});
|
|
2856
|
+
});
|
|
2857
|
+
|
|
2858
|
+
// Initialize page size selector for crash events
|
|
2859
|
+
var pageSizeSelect = document.getElementById('events-page-size');
|
|
2860
|
+
if (pageSizeSelect) {
|
|
2861
|
+
pageSizeSelect.addEventListener('change', function() {
|
|
2862
|
+
updateCrashEventsPagination();
|
|
2863
|
+
});
|
|
2864
|
+
}
|
|
2865
|
+
|
|
2866
|
+
// Initialize pagination for crash events
|
|
2867
|
+
updateCrashEventsPagination();
|
|
2868
|
+
}
|
|
2869
|
+
|
|
2870
|
+
function filterCrashEvents(filterType) {
|
|
2871
|
+
// Simply update pagination, which will handle filtering and display
|
|
2872
|
+
updateCrashEventsPagination();
|
|
2873
|
+
}
|
|
2874
|
+
|
|
2875
|
+
function updateCrashEventsPagination() {
|
|
2876
|
+
var container = document.getElementById('crash-events-container');
|
|
2877
|
+
var pagination = document.getElementById('events-pagination');
|
|
2878
|
+
var pageSizeSelect = document.getElementById('events-page-size');
|
|
2879
|
+
|
|
2880
|
+
if (!container || !pagination || !pageSizeSelect) return;
|
|
2881
|
+
|
|
2882
|
+
// Get all rows and determine which should be visible based on current filter
|
|
2883
|
+
var allRows = Array.from(container.querySelectorAll('.event-row'));
|
|
2884
|
+
var currentFilter = getCurrentEventFilter();
|
|
2885
|
+
|
|
2886
|
+
var visibleRows = allRows.filter(function(row) {
|
|
2887
|
+
var rowType = row.dataset.type;
|
|
2888
|
+
switch(currentFilter) {
|
|
2889
|
+
case 'all-events':
|
|
2890
|
+
return true;
|
|
2891
|
+
case 'crashes-only':
|
|
2892
|
+
return rowType === 'crash';
|
|
2893
|
+
case 'anr-only':
|
|
2894
|
+
return rowType === 'anr';
|
|
2895
|
+
default:
|
|
2896
|
+
return true;
|
|
2897
|
+
}
|
|
2898
|
+
});
|
|
2899
|
+
|
|
2900
|
+
var pageSize = parseInt(pageSizeSelect.value) || 10;
|
|
2901
|
+
var totalPages = Math.ceil(visibleRows.length / pageSize);
|
|
2902
|
+
var currentPage = 1;
|
|
2903
|
+
|
|
2904
|
+
// Hide all rows first
|
|
2905
|
+
allRows.forEach(function(row) {
|
|
2906
|
+
row.style.display = 'none';
|
|
2907
|
+
var detailRow = row.nextElementSibling;
|
|
2908
|
+
if (detailRow && detailRow.classList.contains('collapse')) {
|
|
2909
|
+
detailRow.style.display = 'none';
|
|
2910
|
+
}
|
|
2911
|
+
});
|
|
2912
|
+
|
|
2913
|
+
// Show rows for current page
|
|
2914
|
+
var startIndex = (currentPage - 1) * pageSize;
|
|
2915
|
+
var endIndex = Math.min(startIndex + pageSize, visibleRows.length);
|
|
2916
|
+
|
|
2917
|
+
for (var i = startIndex; i < endIndex; i++) {
|
|
2918
|
+
visibleRows[i].style.display = '';
|
|
2919
|
+
var detailRow = visibleRows[i].nextElementSibling;
|
|
2920
|
+
if (detailRow && detailRow.classList.contains('collapse')) {
|
|
2921
|
+
detailRow.style.display = '';
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
|
|
2925
|
+
// Store current state for pagination callback
|
|
2926
|
+
window.crashEventsState = {
|
|
2927
|
+
visibleRows: visibleRows,
|
|
2928
|
+
pageSize: pageSize,
|
|
2929
|
+
totalPages: totalPages
|
|
2930
|
+
};
|
|
2931
|
+
|
|
2932
|
+
// Update pagination controls
|
|
2933
|
+
updatePaginationControls('events-pagination', currentPage, totalPages, function(page) {
|
|
2934
|
+
showCrashEventsPage(page, window.crashEventsState.visibleRows, window.crashEventsState.pageSize);
|
|
2935
|
+
});
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2938
|
+
function showCrashEventsPage(page, visibleRows, pageSize) {
|
|
2939
|
+
// Hide all visible rows
|
|
2940
|
+
visibleRows.forEach(function(row) {
|
|
2941
|
+
row.style.display = 'none';
|
|
2942
|
+
var detailRow = row.nextElementSibling;
|
|
2943
|
+
if (detailRow && detailRow.classList.contains('collapse')) {
|
|
2944
|
+
detailRow.style.display = 'none';
|
|
2945
|
+
}
|
|
2946
|
+
});
|
|
2947
|
+
|
|
2948
|
+
// Show rows for the specified page
|
|
2949
|
+
var startIndex = (page - 1) * pageSize;
|
|
2950
|
+
var endIndex = Math.min(startIndex + pageSize, visibleRows.length);
|
|
2951
|
+
|
|
2952
|
+
for (var i = startIndex; i < endIndex; i++) {
|
|
2953
|
+
visibleRows[i].style.display = '';
|
|
2954
|
+
var detailRow = visibleRows[i].nextElementSibling;
|
|
2955
|
+
if (detailRow && detailRow.classList.contains('collapse')) {
|
|
2956
|
+
detailRow.style.display = '';
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
|
|
2960
|
+
// Update pagination controls to reflect current page
|
|
2961
|
+
var totalPages = Math.ceil(visibleRows.length / pageSize);
|
|
2962
|
+
var pagination = document.getElementById('events-pagination');
|
|
2963
|
+
if (pagination) {
|
|
2964
|
+
// Update active page
|
|
2965
|
+
var pageItems = pagination.querySelectorAll('.page-item');
|
|
2966
|
+
pageItems.forEach(function(item, index) {
|
|
2967
|
+
// Skip first (previous) and last (next) buttons
|
|
2968
|
+
if (index > 0 && index < pageItems.length - 1) {
|
|
2969
|
+
var pageNum = parseInt(item.textContent);
|
|
2970
|
+
if (pageNum === page) {
|
|
2971
|
+
item.classList.add('active');
|
|
2972
|
+
} else {
|
|
2973
|
+
item.classList.remove('active');
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
});
|
|
2977
|
+
|
|
2978
|
+
// Update previous button state
|
|
2979
|
+
if (pageItems.length > 0) {
|
|
2980
|
+
var prevButton = pageItems[0];
|
|
2981
|
+
if (page === 1) {
|
|
2982
|
+
prevButton.classList.add('disabled');
|
|
2983
|
+
} else {
|
|
2984
|
+
prevButton.classList.remove('disabled');
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2987
|
+
|
|
2988
|
+
// Update next button state
|
|
2989
|
+
if (pageItems.length > 1) {
|
|
2990
|
+
var nextButton = pageItems[pageItems.length - 1];
|
|
2991
|
+
if (page === totalPages) {
|
|
2992
|
+
nextButton.classList.add('disabled');
|
|
2993
|
+
} else {
|
|
2994
|
+
nextButton.classList.remove('disabled');
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
|
|
3000
|
+
function getCurrentEventFilter() {
|
|
3001
|
+
var filterButtons = document.querySelectorAll('input[name="event-filter"]');
|
|
3002
|
+
for (var i = 0; i < filterButtons.length; i++) {
|
|
3003
|
+
if (filterButtons[i].checked) {
|
|
3004
|
+
return filterButtons[i].id;
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
return 'all-events'; // default
|
|
3008
|
+
}
|
|
3009
|
+
|
|
3010
|
+
function updatePaginationControls(paginationId, currentPage, totalPages, onPageClick) {
|
|
3011
|
+
var pagination = document.getElementById(paginationId);
|
|
3012
|
+
if (!pagination) return;
|
|
3013
|
+
|
|
3014
|
+
pagination.innerHTML = '';
|
|
3015
|
+
|
|
3016
|
+
if (totalPages <= 1) {
|
|
3017
|
+
// Hide the entire pagination container when not needed
|
|
3018
|
+
var paginationContainer = pagination.closest('.pagination-container');
|
|
3019
|
+
if (paginationContainer) {
|
|
3020
|
+
paginationContainer.style.display = 'none';
|
|
3021
|
+
}
|
|
3022
|
+
return;
|
|
3023
|
+
}
|
|
3024
|
+
|
|
3025
|
+
// Show the pagination container when needed
|
|
3026
|
+
var paginationContainer = pagination.closest('.pagination-container');
|
|
3027
|
+
if (paginationContainer) {
|
|
3028
|
+
paginationContainer.style.display = '';
|
|
3029
|
+
}
|
|
3030
|
+
|
|
3031
|
+
// Previous button
|
|
3032
|
+
var prevLi = document.createElement('li');
|
|
3033
|
+
prevLi.className = 'page-item' + (currentPage === 1 ? ' disabled' : '');
|
|
3034
|
+
var prevLink = document.createElement('a');
|
|
3035
|
+
prevLink.className = 'page-link';
|
|
3036
|
+
prevLink.href = '#';
|
|
3037
|
+
prevLink.setAttribute('aria-label', 'Previous');
|
|
3038
|
+
prevLink.innerHTML = '<span aria-hidden="true">«</span>';
|
|
3039
|
+
|
|
3040
|
+
prevLink.addEventListener('click', function(e) {
|
|
3041
|
+
e.preventDefault();
|
|
3042
|
+
e.stopPropagation();
|
|
3043
|
+
// Get current page dynamically to avoid stale closure values
|
|
3044
|
+
var currentActivePage = getCurrentPageNumber(paginationId);
|
|
3045
|
+
if (currentActivePage > 1 && !prevLi.classList.contains('disabled')) {
|
|
3046
|
+
onPageClick(currentActivePage - 1);
|
|
3047
|
+
}
|
|
3048
|
+
});
|
|
3049
|
+
|
|
3050
|
+
prevLi.appendChild(prevLink);
|
|
3051
|
+
pagination.appendChild(prevLi);
|
|
3052
|
+
|
|
3053
|
+
// Page numbers
|
|
3054
|
+
for (var i = 1; i <= totalPages; i++) {
|
|
3055
|
+
var li = document.createElement('li');
|
|
3056
|
+
li.className = 'page-item' + (i === currentPage ? ' active' : '');
|
|
3057
|
+
var link = document.createElement('a');
|
|
3058
|
+
link.className = 'page-link';
|
|
3059
|
+
link.href = '#';
|
|
3060
|
+
link.textContent = i;
|
|
3061
|
+
link.addEventListener('click', (function(page) {
|
|
3062
|
+
return function(e) {
|
|
3063
|
+
e.preventDefault();
|
|
3064
|
+
e.stopPropagation();
|
|
3065
|
+
onPageClick(page);
|
|
3066
|
+
};
|
|
3067
|
+
})(i));
|
|
3068
|
+
li.appendChild(link);
|
|
3069
|
+
pagination.appendChild(li);
|
|
3070
|
+
}
|
|
3071
|
+
|
|
3072
|
+
// Next button
|
|
3073
|
+
var nextLi = document.createElement('li');
|
|
3074
|
+
nextLi.className = 'page-item' + (currentPage === totalPages ? ' disabled' : '');
|
|
3075
|
+
var nextLink = document.createElement('a');
|
|
3076
|
+
nextLink.className = 'page-link';
|
|
3077
|
+
nextLink.href = '#';
|
|
3078
|
+
nextLink.setAttribute('aria-label', 'Next');
|
|
3079
|
+
nextLink.innerHTML = '<span aria-hidden="true">»</span>';
|
|
3080
|
+
|
|
3081
|
+
nextLink.addEventListener('click', function(e) {
|
|
3082
|
+
e.preventDefault();
|
|
3083
|
+
e.stopPropagation();
|
|
3084
|
+
// Get current page dynamically to avoid stale closure values
|
|
3085
|
+
var currentActivePage = getCurrentPageNumber(paginationId);
|
|
3086
|
+
if (currentActivePage < totalPages && !nextLi.classList.contains('disabled')) {
|
|
3087
|
+
onPageClick(currentActivePage + 1);
|
|
3088
|
+
}
|
|
3089
|
+
});
|
|
3090
|
+
|
|
3091
|
+
nextLi.appendChild(nextLink);
|
|
3092
|
+
pagination.appendChild(nextLi);
|
|
3093
|
+
}
|
|
3094
|
+
|
|
3095
|
+
function getCurrentPageNumber(paginationId) {
|
|
3096
|
+
var pagination = document.getElementById(paginationId);
|
|
3097
|
+
if (!pagination) return 1;
|
|
3098
|
+
|
|
3099
|
+
var activeItem = pagination.querySelector('.page-item.active');
|
|
3100
|
+
if (activeItem) {
|
|
3101
|
+
var pageText = activeItem.textContent.trim();
|
|
3102
|
+
var pageNum = parseInt(pageText);
|
|
3103
|
+
return isNaN(pageNum) ? 1 : pageNum;
|
|
3104
|
+
}
|
|
3105
|
+
return 1;
|
|
3106
|
+
}
|
|
3107
|
+
|
|
3108
|
+
function copyStackTrace(index) {
|
|
3109
|
+
var stackTraceElement = document.getElementById('stack-trace-' + index);
|
|
3110
|
+
if (!stackTraceElement) return;
|
|
3111
|
+
|
|
3112
|
+
var stackTrace = stackTraceElement.textContent;
|
|
3113
|
+
|
|
3114
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
3115
|
+
navigator.clipboard.writeText(stackTrace).then(function() {
|
|
3116
|
+
showCopyNotification('Stack trace copied to clipboard!');
|
|
3117
|
+
}).catch(function(err) {
|
|
3118
|
+
console.error('Failed to copy stack trace: ', err);
|
|
3119
|
+
showCopyNotification('Failed to copy stack trace', 'error');
|
|
3120
|
+
});
|
|
3121
|
+
} else {
|
|
3122
|
+
// Fallback for older browsers
|
|
3123
|
+
var textArea = document.createElement('textarea');
|
|
3124
|
+
textArea.value = stackTrace;
|
|
3125
|
+
document.body.appendChild(textArea);
|
|
3126
|
+
textArea.select();
|
|
3127
|
+
try {
|
|
3128
|
+
document.execCommand('copy');
|
|
3129
|
+
showCopyNotification('Stack trace copied to clipboard!');
|
|
3130
|
+
} catch (err) {
|
|
3131
|
+
console.error('Fallback copy failed: ', err);
|
|
3132
|
+
showCopyNotification('Failed to copy stack trace', 'error');
|
|
3133
|
+
}
|
|
3134
|
+
document.body.removeChild(textArea);
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
|
|
3138
|
+
function showCopyNotification(message, type) {
|
|
3139
|
+
type = type || 'success';
|
|
3140
|
+
|
|
3141
|
+
// Create notification element
|
|
3142
|
+
var notification = document.createElement('div');
|
|
3143
|
+
notification.className = 'alert alert-' + (type === 'error' ? 'danger' : 'success') + ' position-fixed';
|
|
3144
|
+
notification.style.cssText = 'top: 20px; right: 20px; z-index: 9999; min-width: 300px;';
|
|
3145
|
+
notification.innerHTML = '<i class="bi bi-' + (type === 'error' ? 'exclamation-triangle' : 'check-circle') + '"></i> ' + message;
|
|
3146
|
+
|
|
3147
|
+
document.body.appendChild(notification);
|
|
3148
|
+
|
|
3149
|
+
// Auto remove after 3 seconds
|
|
3150
|
+
setTimeout(function() {
|
|
3151
|
+
if (notification.parentNode) {
|
|
3152
|
+
notification.parentNode.removeChild(notification);
|
|
3153
|
+
}
|
|
3154
|
+
}, 3000);
|
|
3155
|
+
}
|
|
3156
|
+
});
|
|
3157
|
+
|
|
3158
|
+
// Close opened Property Checking Statistics error details when pagination changes
|
|
3159
|
+
function closePropertyErrorDetails(container) {
|
|
3160
|
+
if (!container) return;
|
|
3161
|
+
|
|
3162
|
+
const openDetails = container.querySelectorAll('.property-detail-row.show');
|
|
3163
|
+
openDetails.forEach(function(detailRow) {
|
|
3164
|
+
const collapseInstance = bootstrap.Collapse.getInstance(detailRow) ||
|
|
3165
|
+
new bootstrap.Collapse(detailRow, { toggle: false });
|
|
3166
|
+
collapseInstance.hide();
|
|
3167
|
+
});
|
|
3168
|
+
}
|
|
3169
|
+
|
|
3170
|
+
// Global activity search functions
|
|
3171
|
+
function performActivitySearch(searchInput) {
|
|
3172
|
+
const searchTerm = searchInput.value.toLowerCase().trim();
|
|
3173
|
+
const containerId = searchInput.dataset.target;
|
|
3174
|
+
const itemClass = searchInput.dataset.itemClass;
|
|
3175
|
+
const paginationId = searchInput.dataset.pagination;
|
|
3176
|
+
const pageSizeSelectId = searchInput.dataset.pageSize;
|
|
3177
|
+
|
|
3178
|
+
const container = document.getElementById(containerId);
|
|
3179
|
+
const items = Array.from(container.getElementsByClassName(itemClass));
|
|
3180
|
+
|
|
3181
|
+
// Filter and count matching items
|
|
3182
|
+
let visibleCount = 0;
|
|
3183
|
+
let totalCount = items.length;
|
|
3184
|
+
|
|
3185
|
+
items.forEach(function(item, index) {
|
|
3186
|
+
const activityName = item.querySelector('.activity-name');
|
|
3187
|
+
if (activityName) {
|
|
3188
|
+
const text = activityName.textContent.toLowerCase();
|
|
3189
|
+
const matches = searchTerm === '' || text.includes(searchTerm);
|
|
3190
|
+
|
|
3191
|
+
if (matches) {
|
|
3192
|
+
item.setAttribute('data-search-visible', 'true');
|
|
3193
|
+
visibleCount++;
|
|
3194
|
+
} else {
|
|
3195
|
+
item.setAttribute('data-search-visible', 'false');
|
|
3196
|
+
}
|
|
3197
|
+
} else {
|
|
3198
|
+
// If no activity name found, hide the item
|
|
3199
|
+
item.setAttribute('data-search-visible', 'false');
|
|
3200
|
+
}
|
|
3201
|
+
});
|
|
3202
|
+
|
|
3203
|
+
// Update search results count
|
|
3204
|
+
updateSearchResultsCount(searchInput, visibleCount, totalCount, searchTerm);
|
|
3205
|
+
|
|
3206
|
+
// Re-initialize pagination and go to first page
|
|
3207
|
+
initPagination(containerId, itemClass, paginationId, pageSizeSelectId);
|
|
3208
|
+
|
|
3209
|
+
// Always go to page 1 after search
|
|
3210
|
+
const currentPageSize = document.getElementById(pageSizeSelectId) ?
|
|
3211
|
+
parseInt(document.getElementById(pageSizeSelectId).value) : 10;
|
|
3212
|
+
|
|
3213
|
+
goToPage(1, containerId, itemClass, currentPageSize, paginationId);
|
|
3214
|
+
}
|
|
3215
|
+
|
|
3216
|
+
function updateSearchResultsCount(searchInput, visibleCount, totalCount, searchTerm) {
|
|
3217
|
+
const isTestedTab = searchInput.id.includes('tested');
|
|
3218
|
+
const resultsElementId = isTestedTab ? 'tested-search-results' : 'all-search-results';
|
|
3219
|
+
const resultsElement = document.getElementById(resultsElementId);
|
|
3220
|
+
|
|
3221
|
+
if (resultsElement) {
|
|
3222
|
+
if (searchTerm === '') {
|
|
3223
|
+
resultsElement.innerHTML = `<span class="text-muted">Showing all ${totalCount} activities - Type and click <i class="bi bi-search"></i> or press Enter to search</span>`;
|
|
3224
|
+
} else if (visibleCount === 0) {
|
|
3225
|
+
resultsElement.innerHTML = `<span class="text-danger">No activities found for "${searchTerm}"</span>`;
|
|
3226
|
+
} else if (visibleCount === totalCount) {
|
|
3227
|
+
resultsElement.innerHTML = `<span class="text-success">All ${totalCount} activities match "${searchTerm}"</span>`;
|
|
3228
|
+
} else {
|
|
3229
|
+
resultsElement.innerHTML = `<span class="text-info">Found ${visibleCount} of ${totalCount} activities for "${searchTerm}"</span>`;
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
|
|
3234
|
+
// Activity searching function
|
|
3235
|
+
function initActivitySearching() {
|
|
3236
|
+
const searchInputs = document.querySelectorAll('.activity-search-input');
|
|
3237
|
+
const searchButtons = document.querySelectorAll('.search-btn');
|
|
3238
|
+
const clearButtons = document.querySelectorAll('.search-clear-btn');
|
|
3239
|
+
|
|
3240
|
+
// Initialize search functionality for each input
|
|
3241
|
+
searchInputs.forEach(function(input) {
|
|
3242
|
+
// Search on Enter key
|
|
3243
|
+
input.addEventListener('keydown', function(e) {
|
|
3244
|
+
if (e.key === 'Enter') {
|
|
3245
|
+
e.preventDefault();
|
|
3246
|
+
performActivitySearch(this);
|
|
3247
|
+
} else if (e.key === 'Escape') {
|
|
3248
|
+
clearActivitySearch(this);
|
|
3249
|
+
}
|
|
3250
|
+
});
|
|
3251
|
+
});
|
|
3252
|
+
|
|
3253
|
+
// Initialize search button functionality
|
|
3254
|
+
searchButtons.forEach(function(button) {
|
|
3255
|
+
button.addEventListener('click', function() {
|
|
3256
|
+
const targetInputId = this.dataset.target;
|
|
3257
|
+
const targetInput = document.getElementById(targetInputId);
|
|
3258
|
+
if (targetInput) {
|
|
3259
|
+
performActivitySearch(targetInput);
|
|
3260
|
+
}
|
|
3261
|
+
});
|
|
3262
|
+
});
|
|
3263
|
+
|
|
3264
|
+
// Initialize clear button functionality
|
|
3265
|
+
clearButtons.forEach(function(button) {
|
|
3266
|
+
button.addEventListener('click', function() {
|
|
3267
|
+
const targetInputId = this.dataset.target;
|
|
3268
|
+
const targetInput = document.getElementById(targetInputId);
|
|
3269
|
+
if (targetInput) {
|
|
3270
|
+
clearActivitySearch(targetInput);
|
|
3271
|
+
}
|
|
3272
|
+
});
|
|
3273
|
+
});
|
|
3274
|
+
|
|
3275
|
+
function clearActivitySearch(searchInput) {
|
|
3276
|
+
searchInput.value = '';
|
|
3277
|
+
performActivitySearch(searchInput);
|
|
3278
|
+
searchInput.focus();
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
|
|
3282
|
+
function getCurrentPropertyKindFilter() {
|
|
3283
|
+
const selected = document.querySelector('input[name="property-kind-filter"]:checked');
|
|
3284
|
+
if (!selected) {
|
|
3285
|
+
return 'all';
|
|
3286
|
+
}
|
|
3287
|
+
if (selected.id === 'property-kind-property') {
|
|
3288
|
+
return 'property';
|
|
3289
|
+
}
|
|
3290
|
+
if (selected.id === 'property-kind-invariant') {
|
|
3291
|
+
return 'invariant';
|
|
3292
|
+
}
|
|
3293
|
+
return 'all';
|
|
3294
|
+
}
|
|
3295
|
+
|
|
3296
|
+
function updatePropertySearchResults(visibleCount, totalCount, searchTerm) {
|
|
3297
|
+
const resultsElement = document.getElementById('property-search-results');
|
|
3298
|
+
|
|
3299
|
+
if (resultsElement) {
|
|
3300
|
+
if (searchTerm === '') {
|
|
3301
|
+
resultsElement.innerHTML = `<span class="text-muted">Showing all ${totalCount} properties - Type and click <i class="bi bi-search"></i> or press Enter to search</span>`;
|
|
3302
|
+
} else if (visibleCount === 0) {
|
|
3303
|
+
resultsElement.innerHTML = `<span class="text-danger">No properties found for "${searchTerm}"</span>`;
|
|
3304
|
+
} else if (visibleCount === totalCount) {
|
|
3305
|
+
resultsElement.innerHTML = `<span class="text-success">All ${totalCount} properties match "${searchTerm}"</span>`;
|
|
3306
|
+
} else {
|
|
3307
|
+
resultsElement.innerHTML = `<span class="text-info">Found ${visibleCount} of ${totalCount} properties for "${searchTerm}"</span>`;
|
|
3308
|
+
}
|
|
3309
|
+
}
|
|
3310
|
+
}
|
|
3311
|
+
|
|
3312
|
+
function updatePropertyHeaderSummary(summary) {
|
|
3313
|
+
const totalEl = document.getElementById('property-stats-total-properties');
|
|
3314
|
+
if (totalEl) {
|
|
3315
|
+
totalEl.textContent = summary.total;
|
|
3316
|
+
}
|
|
3317
|
+
const precondEl = document.getElementById('property-stats-total-precond');
|
|
3318
|
+
if (precondEl) {
|
|
3319
|
+
precondEl.textContent = summary.precond;
|
|
3320
|
+
}
|
|
3321
|
+
const executedEl = document.getElementById('property-stats-total-executed');
|
|
3322
|
+
if (executedEl) {
|
|
3323
|
+
executedEl.textContent = summary.executed;
|
|
3324
|
+
}
|
|
3325
|
+
const passEl = document.getElementById('property-stats-total-pass');
|
|
3326
|
+
if (passEl) {
|
|
3327
|
+
passEl.textContent = summary.pass;
|
|
3328
|
+
}
|
|
3329
|
+
const failEl = document.getElementById('property-stats-total-fail');
|
|
3330
|
+
if (failEl) {
|
|
3331
|
+
failEl.textContent = summary.fail;
|
|
3332
|
+
}
|
|
3333
|
+
const errorEl = document.getElementById('property-stats-total-error');
|
|
3334
|
+
if (errorEl) {
|
|
3335
|
+
errorEl.textContent = summary.error;
|
|
3336
|
+
}
|
|
3337
|
+
}
|
|
3338
|
+
|
|
3339
|
+
function applyPropertyFilters() {
|
|
3340
|
+
const searchInput = document.getElementById('property-stats-search');
|
|
3341
|
+
const rawSearchTerm = searchInput ? searchInput.value.trim() : '';
|
|
3342
|
+
const searchTerm = rawSearchTerm.toLowerCase();
|
|
3343
|
+
const container = document.getElementById('property-stats-container');
|
|
3344
|
+
|
|
3345
|
+
if (!container) {
|
|
3346
|
+
return;
|
|
3347
|
+
}
|
|
3348
|
+
|
|
3349
|
+
const items = Array.from(container.getElementsByClassName('property-stat-row'));
|
|
3350
|
+
const kindFilter = getCurrentPropertyKindFilter();
|
|
3351
|
+
let visibleCount = 0;
|
|
3352
|
+
let totalCount = 0;
|
|
3353
|
+
const summary = {
|
|
3354
|
+
total: 0,
|
|
3355
|
+
precond: 0,
|
|
3356
|
+
executed: 0,
|
|
3357
|
+
pass: 0,
|
|
3358
|
+
fail: 0,
|
|
3359
|
+
error: 0,
|
|
3360
|
+
};
|
|
3361
|
+
|
|
3362
|
+
items.forEach(function(item) {
|
|
3363
|
+
const rowKind = (item.dataset.kind || 'unknown').toLowerCase();
|
|
3364
|
+
const kindMatches = kindFilter === 'all' || rowKind === kindFilter;
|
|
3365
|
+
item.setAttribute('data-kind-visible', kindMatches ? 'true' : 'false');
|
|
3366
|
+
if (kindMatches) {
|
|
3367
|
+
totalCount += 1;
|
|
3368
|
+
}
|
|
3369
|
+
|
|
3370
|
+
const propertyNameElement = item.querySelector('.badge-custom');
|
|
3371
|
+
const propertyName = propertyNameElement ? propertyNameElement.textContent.toLowerCase() : '';
|
|
3372
|
+
const searchMatches = searchTerm === '' || propertyName.includes(searchTerm);
|
|
3373
|
+
item.setAttribute('data-search-visible', searchMatches ? 'true' : 'false');
|
|
3374
|
+
if (kindMatches && searchMatches) {
|
|
3375
|
+
visibleCount += 1;
|
|
3376
|
+
summary.total += 1;
|
|
3377
|
+
summary.precond += parseInt(item.dataset.precondSatisfied || '0', 10);
|
|
3378
|
+
summary.executed += parseInt(item.dataset.executed || '0', 10);
|
|
3379
|
+
summary.pass += parseInt(item.dataset.pass || '0', 10);
|
|
3380
|
+
summary.fail += parseInt(item.dataset.fails || '0', 10);
|
|
3381
|
+
summary.error += parseInt(item.dataset.errors || '0', 10);
|
|
3382
|
+
}
|
|
3383
|
+
});
|
|
3384
|
+
|
|
3385
|
+
updatePropertySearchResults(visibleCount, totalCount, rawSearchTerm);
|
|
3386
|
+
|
|
3387
|
+
const headerSummary = {
|
|
3388
|
+
total: summary.total,
|
|
3389
|
+
precond: summary.precond,
|
|
3390
|
+
executed: summary.executed,
|
|
3391
|
+
pass: summary.pass,
|
|
3392
|
+
fail: summary.fail,
|
|
3393
|
+
error: summary.error,
|
|
3394
|
+
};
|
|
3395
|
+
if (kindFilter === 'invariant') {
|
|
3396
|
+
headerSummary.precond = '/';
|
|
3397
|
+
headerSummary.executed = '/';
|
|
3398
|
+
headerSummary.pass = '/';
|
|
3399
|
+
}
|
|
3400
|
+
updatePropertyHeaderSummary(headerSummary);
|
|
3401
|
+
|
|
3402
|
+
initPagination('property-stats-container', 'property-stat-row', 'stats-pagination', 'stats-page-size');
|
|
3403
|
+
const currentPageSize = document.getElementById('stats-page-size') ?
|
|
3404
|
+
parseInt(document.getElementById('stats-page-size').value) : 10;
|
|
3405
|
+
goToPage(1, 'property-stats-container', 'property-stat-row', currentPageSize, 'stats-pagination');
|
|
3406
|
+
}
|
|
3407
|
+
|
|
3408
|
+
function initPropertyKindFilter() {
|
|
3409
|
+
const filterButtons = document.querySelectorAll('input[name="property-kind-filter"]');
|
|
3410
|
+
if (!filterButtons.length) {
|
|
3411
|
+
return;
|
|
3412
|
+
}
|
|
3413
|
+
filterButtons.forEach(function(button) {
|
|
3414
|
+
button.addEventListener('change', function() {
|
|
3415
|
+
applyPropertyFilters();
|
|
3416
|
+
});
|
|
3417
|
+
});
|
|
3418
|
+
applyPropertyFilters();
|
|
3419
|
+
}
|
|
3420
|
+
|
|
3421
|
+
// Property statistics searching function
|
|
3422
|
+
function initPropertySearching() {
|
|
3423
|
+
const searchInput = document.getElementById('property-stats-search');
|
|
3424
|
+
const searchButton = document.getElementById('property-search-btn');
|
|
3425
|
+
|
|
3426
|
+
if (!searchInput) return;
|
|
3427
|
+
|
|
3428
|
+
// Search on Enter key and clear on Escape
|
|
3429
|
+
searchInput.addEventListener('keydown', function(e) {
|
|
3430
|
+
if (e.key === 'Enter') {
|
|
3431
|
+
e.preventDefault();
|
|
3432
|
+
applyPropertyFilters();
|
|
3433
|
+
} else if (e.key === 'Escape') {
|
|
3434
|
+
searchInput.value = '';
|
|
3435
|
+
applyPropertyFilters();
|
|
3436
|
+
searchInput.focus();
|
|
3437
|
+
}
|
|
3438
|
+
});
|
|
3439
|
+
|
|
3440
|
+
// Search button functionality
|
|
3441
|
+
if (searchButton) {
|
|
3442
|
+
searchButton.addEventListener('click', function() {
|
|
3443
|
+
applyPropertyFilters();
|
|
3444
|
+
});
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
|
|
3448
|
+
function initViolationFilter() {
|
|
3449
|
+
const statusSelect = document.getElementById('violations-status-filter');
|
|
3450
|
+
const kindSelect = document.getElementById('violations-kind-filter');
|
|
3451
|
+
const container = document.getElementById('property-violations-container');
|
|
3452
|
+
|
|
3453
|
+
if (!container) return;
|
|
3454
|
+
|
|
3455
|
+
function applyViolationFilters() {
|
|
3456
|
+
const statusValue = statusSelect ? statusSelect.value : 'all';
|
|
3457
|
+
const kindValue = kindSelect ? kindSelect.value : 'all';
|
|
3458
|
+
const rows = Array.from(container.getElementsByClassName('property-violation-row'));
|
|
3459
|
+
|
|
3460
|
+
rows.forEach(function(row) {
|
|
3461
|
+
const status = row.getAttribute('data-status') || 'fail';
|
|
3462
|
+
const kind = (row.getAttribute('data-kind') || 'unknown').toLowerCase();
|
|
3463
|
+
const statusMatch = statusValue === 'all' || status === statusValue;
|
|
3464
|
+
const kindMatch = kindValue === 'all' || kind === kindValue;
|
|
3465
|
+
row.setAttribute('data-search-visible', statusMatch && kindMatch ? 'true' : 'false');
|
|
3466
|
+
});
|
|
3467
|
+
|
|
3468
|
+
initPagination('property-violations-container', 'property-violation-row', 'violations-pagination', 'violations-page-size');
|
|
3469
|
+
|
|
3470
|
+
const currentPageSize = document.getElementById('violations-page-size') ?
|
|
3471
|
+
parseInt(document.getElementById('violations-page-size').value) : 10;
|
|
3472
|
+
goToPage(1, 'property-violations-container', 'property-violation-row', currentPageSize, 'violations-pagination');
|
|
3473
|
+
}
|
|
3474
|
+
|
|
3475
|
+
if (statusSelect) {
|
|
3476
|
+
statusSelect.addEventListener('change', applyViolationFilters);
|
|
3477
|
+
}
|
|
3478
|
+
if (kindSelect) {
|
|
3479
|
+
kindSelect.addEventListener('change', applyViolationFilters);
|
|
3480
|
+
}
|
|
3481
|
+
applyViolationFilters();
|
|
3482
|
+
}
|
|
3483
|
+
|
|
3484
|
+
// Global pagination and navigation functions
|
|
3485
|
+
function goToPage(pageNumber, containerId, itemClass, itemsPerPage, paginationId) {
|
|
3486
|
+
const container = document.getElementById(containerId);
|
|
3487
|
+
const allItems = Array.from(container.getElementsByClassName(itemClass));
|
|
3488
|
+
const paginationElement = document.getElementById(paginationId);
|
|
3489
|
+
|
|
3490
|
+
if (containerId === 'property-stats-container') {
|
|
3491
|
+
closePropertyErrorDetails(container);
|
|
3492
|
+
}
|
|
3493
|
+
|
|
3494
|
+
// Get items that should be visible based on search filter
|
|
3495
|
+
const filteredItems = allItems.filter(item => {
|
|
3496
|
+
const searchVisible = item.getAttribute('data-search-visible');
|
|
3497
|
+
const kindVisible = item.getAttribute('data-kind-visible');
|
|
3498
|
+
const shouldShow = (searchVisible === null || searchVisible === 'true') &&
|
|
3499
|
+
(kindVisible === null || kindVisible === 'true');
|
|
3500
|
+
return shouldShow;
|
|
3501
|
+
});
|
|
3502
|
+
|
|
3503
|
+
// Update pagination active state
|
|
3504
|
+
if (paginationElement) {
|
|
3505
|
+
const pageItems = paginationElement.getElementsByClassName('page-item');
|
|
3506
|
+
for (let i = 0; i < pageItems.length; i++) {
|
|
3507
|
+
if (pageItems[i].textContent.trim() === pageNumber.toString()) {
|
|
3508
|
+
pageItems[i].className = 'page-item active';
|
|
3509
|
+
} else if (pageItems[i].textContent.trim() !== '«' && pageItems[i].textContent.trim() !== '»') {
|
|
3510
|
+
pageItems[i].className = 'page-item';
|
|
3511
|
+
}
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
|
|
3515
|
+
// Hide all items first
|
|
3516
|
+
allItems.forEach((item, index) => {
|
|
3517
|
+
item.style.display = 'none';
|
|
3518
|
+
});
|
|
3519
|
+
|
|
3520
|
+
// Show items for current page (only from filtered items)
|
|
3521
|
+
const startIndex = (pageNumber - 1) * itemsPerPage;
|
|
3522
|
+
const endIndex = Math.min(startIndex + itemsPerPage, filteredItems.length);
|
|
3523
|
+
|
|
3524
|
+
for (let i = startIndex; i < endIndex; i++) {
|
|
3525
|
+
if (filteredItems[i]) {
|
|
3526
|
+
filteredItems[i].style.display = '';
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3529
|
+
}
|
|
3530
|
+
|
|
3531
|
+
function initPagination(containerId, itemClass, paginationId, pageSizeSelectId) {
|
|
3532
|
+
const container = document.getElementById(containerId);
|
|
3533
|
+
const pageSizeSelect = document.getElementById(pageSizeSelectId);
|
|
3534
|
+
if (!container) return;
|
|
3535
|
+
|
|
3536
|
+
// Remove existing event listener to prevent duplicate bindings
|
|
3537
|
+
if (pageSizeSelect && !pageSizeSelect.hasAttribute('data-listener-bound')) {
|
|
3538
|
+
pageSizeSelect.addEventListener('change', function() {
|
|
3539
|
+
const newItemsPerPage = parseInt(this.value);
|
|
3540
|
+
renderPagination();
|
|
3541
|
+
goToPage(1, containerId, itemClass, newItemsPerPage, paginationId);
|
|
3542
|
+
});
|
|
3543
|
+
// Mark as having listener bound
|
|
3544
|
+
pageSizeSelect.setAttribute('data-listener-bound', 'true');
|
|
3545
|
+
}
|
|
3546
|
+
|
|
3547
|
+
function renderPagination() {
|
|
3548
|
+
const allItems = Array.from(container.getElementsByClassName(itemClass));
|
|
3549
|
+
|
|
3550
|
+
// Always get current page size from select element
|
|
3551
|
+
const currentPageSizeSelect = document.getElementById(pageSizeSelectId);
|
|
3552
|
+
const currentItemsPerPage = currentPageSizeSelect ? parseInt(currentPageSizeSelect.value) : 10;
|
|
3553
|
+
|
|
3554
|
+
// Use same filtering logic as goToPage function
|
|
3555
|
+
const filteredItems = allItems.filter(item => {
|
|
3556
|
+
const searchVisible = item.getAttribute('data-search-visible');
|
|
3557
|
+
const kindVisible = item.getAttribute('data-kind-visible');
|
|
3558
|
+
// If no filters are applied, show all items
|
|
3559
|
+
return (searchVisible === null || searchVisible === 'true') &&
|
|
3560
|
+
(kindVisible === null || kindVisible === 'true');
|
|
3561
|
+
});
|
|
3562
|
+
|
|
3563
|
+
const totalItems = filteredItems.length;
|
|
3564
|
+
const totalPages = Math.max(1, Math.ceil(totalItems / currentItemsPerPage));
|
|
3565
|
+
|
|
3566
|
+
// Create pagination
|
|
3567
|
+
const paginationElement = document.getElementById(paginationId);
|
|
3568
|
+
if (!paginationElement) return;
|
|
3569
|
+
|
|
3570
|
+
// Clear pagination
|
|
3571
|
+
paginationElement.innerHTML = '';
|
|
3572
|
+
|
|
3573
|
+
// Don't show pagination if there's only one page or no items
|
|
3574
|
+
if (totalPages <= 1) {
|
|
3575
|
+
// Hide the entire pagination container when not needed
|
|
3576
|
+
const paginationContainer = paginationElement.closest('.pagination-container');
|
|
3577
|
+
if (paginationContainer) {
|
|
3578
|
+
paginationContainer.style.display = 'none';
|
|
3579
|
+
}
|
|
3580
|
+
return;
|
|
3581
|
+
}
|
|
3582
|
+
|
|
3583
|
+
// Show the pagination container when needed
|
|
3584
|
+
const paginationContainer = paginationElement.closest('.pagination-container');
|
|
3585
|
+
if (paginationContainer) {
|
|
3586
|
+
paginationContainer.style.display = '';
|
|
3587
|
+
}
|
|
3588
|
+
|
|
3589
|
+
// Previous button
|
|
3590
|
+
const prevLi = document.createElement('li');
|
|
3591
|
+
prevLi.className = 'page-item disabled';
|
|
3592
|
+
prevLi.innerHTML = '<a class="page-link" href="#" aria-label="Previous"><span aria-hidden="true">«</span></a>';
|
|
3593
|
+
paginationElement.appendChild(prevLi);
|
|
3594
|
+
|
|
3595
|
+
// Add page numbers
|
|
3596
|
+
for (let i = 1; i <= totalPages; i++) {
|
|
3597
|
+
const pageLi = document.createElement('li');
|
|
3598
|
+
pageLi.className = i === 1 ? 'page-item active' : 'page-item';
|
|
3599
|
+
pageLi.innerHTML = `<a class="page-link" href="#">${i}</a>`;
|
|
3600
|
+
pageLi.addEventListener('click', function(e) {
|
|
3601
|
+
e.preventDefault();
|
|
3602
|
+
goToPage(i, containerId, itemClass, currentItemsPerPage, paginationId);
|
|
3603
|
+
});
|
|
3604
|
+
paginationElement.appendChild(pageLi);
|
|
3605
|
+
}
|
|
3606
|
+
|
|
3607
|
+
// Next button
|
|
3608
|
+
const nextLi = document.createElement('li');
|
|
3609
|
+
nextLi.className = totalPages <= 1 ? 'page-item disabled' : 'page-item';
|
|
3610
|
+
nextLi.innerHTML = '<a class="page-link" href="#" aria-label="Next"><span aria-hidden="true">»</span></a>';
|
|
3611
|
+
paginationElement.appendChild(nextLi);
|
|
3612
|
+
|
|
3613
|
+
// Add event listeners to prev/next buttons
|
|
3614
|
+
if (paginationElement.firstChild) {
|
|
3615
|
+
paginationElement.firstChild.addEventListener('click', function(e) {
|
|
3616
|
+
e.preventDefault();
|
|
3617
|
+
const activePage = paginationElement.querySelector('.active');
|
|
3618
|
+
if (activePage && activePage.previousElementSibling && activePage.previousElementSibling.previousElementSibling) {
|
|
3619
|
+
const pageNum = parseInt(activePage.textContent) - 1;
|
|
3620
|
+
if (pageNum >= 1) {
|
|
3621
|
+
goToPage(pageNum, containerId, itemClass, currentItemsPerPage, paginationId);
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
});
|
|
3625
|
+
}
|
|
3626
|
+
|
|
3627
|
+
if (paginationElement.lastChild) {
|
|
3628
|
+
paginationElement.lastChild.addEventListener('click', function(e) {
|
|
3629
|
+
e.preventDefault();
|
|
3630
|
+
const activePage = paginationElement.querySelector('.active');
|
|
3631
|
+
if (activePage && activePage.nextElementSibling && activePage.nextElementSibling.nextElementSibling) {
|
|
3632
|
+
const pageNum = parseInt(activePage.textContent) + 1;
|
|
3633
|
+
if (pageNum <= totalPages) {
|
|
3634
|
+
goToPage(pageNum, containerId, itemClass, currentItemsPerPage, paginationId);
|
|
3635
|
+
}
|
|
3636
|
+
}
|
|
3637
|
+
});
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
|
|
3641
|
+
// Initial render
|
|
3642
|
+
renderPagination();
|
|
3643
|
+
// Always call goToPage to ensure items are displayed correctly, even if no pagination is shown
|
|
3644
|
+
const currentItemsPerPage = pageSizeSelect ? parseInt(pageSizeSelect.value) : 10;
|
|
3645
|
+
goToPage(1, containerId, itemClass, currentItemsPerPage, paginationId);
|
|
3646
|
+
}
|
|
3647
|
+
|
|
3648
|
+
// Activities table searching function
|
|
3649
|
+
function initActivitiesSearching() {
|
|
3650
|
+
const searchInput = document.getElementById('activities-search');
|
|
3651
|
+
const searchButton = document.getElementById('activities-search-btn');
|
|
3652
|
+
|
|
3653
|
+
if (!searchInput) return;
|
|
3654
|
+
|
|
3655
|
+
// Search on Enter key and clear on Escape
|
|
3656
|
+
searchInput.addEventListener('keydown', function(e) {
|
|
3657
|
+
if (e.key === 'Enter') {
|
|
3658
|
+
e.preventDefault();
|
|
3659
|
+
performActivitiesSearch(this);
|
|
3660
|
+
} else if (e.key === 'Escape') {
|
|
3661
|
+
clearActivitiesSearch(this);
|
|
3662
|
+
}
|
|
3663
|
+
});
|
|
3664
|
+
|
|
3665
|
+
// Search button functionality
|
|
3666
|
+
if (searchButton) {
|
|
3667
|
+
searchButton.addEventListener('click', function() {
|
|
3668
|
+
performActivitiesSearch(searchInput);
|
|
3669
|
+
});
|
|
3670
|
+
}
|
|
3671
|
+
|
|
3672
|
+
function performActivitiesSearch(searchInput) {
|
|
3673
|
+
const searchTerm = searchInput.value.toLowerCase().trim();
|
|
3674
|
+
const container = document.getElementById('activities-container');
|
|
3675
|
+
const rows = container.querySelectorAll('.activity-row');
|
|
3676
|
+
const resultsCount = document.getElementById('activities-search-results');
|
|
3677
|
+
|
|
3678
|
+
let visibleCount = 0;
|
|
3679
|
+
|
|
3680
|
+
rows.forEach(function(row) {
|
|
3681
|
+
const activityName = row.querySelector('.activity-name').textContent.toLowerCase();
|
|
3682
|
+
|
|
3683
|
+
// If no search term, show all rows
|
|
3684
|
+
if (!searchTerm) {
|
|
3685
|
+
row.removeAttribute('data-search-visible');
|
|
3686
|
+
visibleCount++;
|
|
3687
|
+
} else {
|
|
3688
|
+
const matches = activityName.includes(searchTerm);
|
|
3689
|
+
if (matches) {
|
|
3690
|
+
row.setAttribute('data-search-visible', 'true');
|
|
3691
|
+
visibleCount++;
|
|
3692
|
+
} else {
|
|
3693
|
+
row.setAttribute('data-search-visible', 'false');
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
});
|
|
3697
|
+
|
|
3698
|
+
console.log('Search performed:', {searchTerm, visibleCount, totalRows: rows.length});
|
|
3699
|
+
|
|
3700
|
+
// Update results count
|
|
3701
|
+
if (resultsCount) {
|
|
3702
|
+
if (searchTerm) {
|
|
3703
|
+
resultsCount.textContent = `Found ${visibleCount} of ${rows.length} activities`;
|
|
3704
|
+
} else {
|
|
3705
|
+
resultsCount.textContent = '';
|
|
3706
|
+
}
|
|
3707
|
+
}
|
|
3708
|
+
|
|
3709
|
+
// Immediately show/hide rows based on search before pagination update
|
|
3710
|
+
rows.forEach(function(row) {
|
|
3711
|
+
const searchVisible = row.getAttribute('data-search-visible');
|
|
3712
|
+
if (searchVisible === null || searchVisible === 'true') {
|
|
3713
|
+
row.style.display = '';
|
|
3714
|
+
} else {
|
|
3715
|
+
row.style.display = 'none';
|
|
3716
|
+
}
|
|
3717
|
+
});
|
|
3718
|
+
|
|
3719
|
+
console.log('Rows visibility updated, calling pagination update...');
|
|
3720
|
+
|
|
3721
|
+
// Update pagination after search
|
|
3722
|
+
updateActivitiesPagination();
|
|
3723
|
+
}
|
|
3724
|
+
|
|
3725
|
+
function clearActivitiesSearch(searchInput) {
|
|
3726
|
+
searchInput.value = '';
|
|
3727
|
+
performActivitiesSearch(searchInput);
|
|
3728
|
+
searchInput.focus();
|
|
3729
|
+
}
|
|
3730
|
+
}
|
|
3731
|
+
|
|
3732
|
+
// Initialize Activities page size selector
|
|
3733
|
+
function initActivitiesPageSize() {
|
|
3734
|
+
const pageSizeSelect = document.getElementById('activities-page-size');
|
|
3735
|
+
|
|
3736
|
+
if (!pageSizeSelect) return;
|
|
3737
|
+
|
|
3738
|
+
// Remove existing event listener to prevent duplicate bindings
|
|
3739
|
+
if (!pageSizeSelect.hasAttribute('data-activities-listener-bound')) {
|
|
3740
|
+
pageSizeSelect.addEventListener('change', function() {
|
|
3741
|
+
console.log('Activities page size changed to:', this.value);
|
|
3742
|
+
updateActivitiesPagination();
|
|
3743
|
+
});
|
|
3744
|
+
// Mark as having listener bound
|
|
3745
|
+
pageSizeSelect.setAttribute('data-activities-listener-bound', 'true');
|
|
3746
|
+
}
|
|
3747
|
+
}
|
|
3748
|
+
|
|
3749
|
+
// Activities table sorting function
|
|
3750
|
+
function initActivitiesSorting() {
|
|
3751
|
+
const sortIcon = document.getElementById('visit-count-sort');
|
|
3752
|
+
|
|
3753
|
+
if (!sortIcon) return;
|
|
3754
|
+
|
|
3755
|
+
sortIcon.addEventListener('click', function(e) {
|
|
3756
|
+
e.preventDefault();
|
|
3757
|
+
e.stopPropagation();
|
|
3758
|
+
|
|
3759
|
+
console.log('Sort icon clicked, current order:', this.dataset.order);
|
|
3760
|
+
|
|
3761
|
+
const currentOrder = this.dataset.order || 'none';
|
|
3762
|
+
const container = document.getElementById('activities-container');
|
|
3763
|
+
const rows = Array.from(container.querySelectorAll('.activity-row'));
|
|
3764
|
+
|
|
3765
|
+
console.log('Found rows:', rows.length);
|
|
3766
|
+
|
|
3767
|
+
// Reset only activities sort icons (not property stats sort icons)
|
|
3768
|
+
document.querySelectorAll('.activities-sort-icon').forEach(icon => {
|
|
3769
|
+
if (icon !== this) {
|
|
3770
|
+
icon.className = 'bi bi-arrow-down-up text-muted sort-icon activities-sort-icon';
|
|
3771
|
+
icon.dataset.order = 'none';
|
|
3772
|
+
}
|
|
3773
|
+
});
|
|
3774
|
+
|
|
3775
|
+
let newOrder;
|
|
3776
|
+
if (currentOrder === 'none' || currentOrder === 'desc') {
|
|
3777
|
+
newOrder = 'asc';
|
|
3778
|
+
this.className = 'bi bi-arrow-up text-primary sort-icon activities-sort-icon';
|
|
3779
|
+
} else {
|
|
3780
|
+
newOrder = 'desc';
|
|
3781
|
+
this.className = 'bi bi-arrow-down text-primary sort-icon activities-sort-icon';
|
|
3782
|
+
}
|
|
3783
|
+
|
|
3784
|
+
this.dataset.order = newOrder;
|
|
3785
|
+
console.log('New order set to:', newOrder);
|
|
3786
|
+
|
|
3787
|
+
// Sort rows
|
|
3788
|
+
rows.sort((a, b) => {
|
|
3789
|
+
const aCount = parseInt(a.dataset.visitCount) || 0;
|
|
3790
|
+
const bCount = parseInt(b.dataset.visitCount) || 0;
|
|
3791
|
+
|
|
3792
|
+
if (newOrder === 'asc') {
|
|
3793
|
+
return aCount - bCount;
|
|
3794
|
+
} else {
|
|
3795
|
+
return bCount - aCount;
|
|
3796
|
+
}
|
|
3797
|
+
});
|
|
3798
|
+
|
|
3799
|
+
// Clear container and re-append sorted rows
|
|
3800
|
+
container.innerHTML = '';
|
|
3801
|
+
rows.forEach(row => container.appendChild(row));
|
|
3802
|
+
|
|
3803
|
+
console.log('Sorting completed');
|
|
3804
|
+
|
|
3805
|
+
// Properly update pagination after sorting
|
|
3806
|
+
updateActivitiesPagination();
|
|
3807
|
+
});
|
|
3808
|
+
}
|
|
3809
|
+
|
|
3810
|
+
// Function to update Activities pagination after sorting
|
|
3811
|
+
function updateActivitiesPagination() {
|
|
3812
|
+
const container = document.getElementById('activities-container');
|
|
3813
|
+
const pageSizeSelect = document.getElementById('activities-page-size');
|
|
3814
|
+
const paginationElement = document.getElementById('activities-pagination');
|
|
3815
|
+
|
|
3816
|
+
if (!container || !paginationElement) return;
|
|
3817
|
+
|
|
3818
|
+
const allRows = Array.from(container.querySelectorAll('.activity-row'));
|
|
3819
|
+
const itemsPerPage = pageSizeSelect ? parseInt(pageSizeSelect.value) : 10;
|
|
3820
|
+
|
|
3821
|
+
// Filter visible rows (considering search)
|
|
3822
|
+
const visibleRows = allRows.filter(row => {
|
|
3823
|
+
const searchVisible = row.getAttribute('data-search-visible');
|
|
3824
|
+
return searchVisible === null || searchVisible === 'true';
|
|
3825
|
+
});
|
|
3826
|
+
|
|
3827
|
+
const totalItems = visibleRows.length;
|
|
3828
|
+
const totalPages = Math.max(1, Math.ceil(totalItems / itemsPerPage));
|
|
3829
|
+
|
|
3830
|
+
console.log('Updating pagination:', {totalItems, itemsPerPage, totalPages});
|
|
3831
|
+
|
|
3832
|
+
// Clear pagination
|
|
3833
|
+
paginationElement.innerHTML = '';
|
|
3834
|
+
|
|
3835
|
+
// Find pagination container
|
|
3836
|
+
const paginationContainer = paginationElement.closest('.d-flex');
|
|
3837
|
+
|
|
3838
|
+
// Don't show pagination if there's only one page or no items
|
|
3839
|
+
if (totalPages <= 1) {
|
|
3840
|
+
if (paginationContainer) {
|
|
3841
|
+
paginationContainer.style.display = 'none';
|
|
3842
|
+
}
|
|
3843
|
+
// Show visible rows and hide invisible rows
|
|
3844
|
+
allRows.forEach(row => {
|
|
3845
|
+
const searchVisible = row.getAttribute('data-search-visible');
|
|
3846
|
+
if (searchVisible === null || searchVisible === 'true') {
|
|
3847
|
+
row.style.display = '';
|
|
3848
|
+
} else {
|
|
3849
|
+
row.style.display = 'none';
|
|
3850
|
+
}
|
|
3851
|
+
});
|
|
3852
|
+
return;
|
|
3853
|
+
}
|
|
3854
|
+
|
|
3855
|
+
// Show the pagination container when needed
|
|
3856
|
+
if (paginationContainer) {
|
|
3857
|
+
paginationContainer.style.display = '';
|
|
3858
|
+
}
|
|
3859
|
+
|
|
3860
|
+
// Create Previous button
|
|
3861
|
+
const prevLi = document.createElement('li');
|
|
3862
|
+
prevLi.className = 'page-item';
|
|
3863
|
+
prevLi.id = 'activities-prev-page';
|
|
3864
|
+
|
|
3865
|
+
const prevA = document.createElement('a');
|
|
3866
|
+
prevA.className = 'page-link';
|
|
3867
|
+
prevA.href = '#';
|
|
3868
|
+
prevA.innerHTML = '«';
|
|
3869
|
+
prevA.addEventListener('click', function(e) {
|
|
3870
|
+
e.preventDefault();
|
|
3871
|
+
const currentPage = getCurrentActivitiesPage();
|
|
3872
|
+
if (currentPage > 1) {
|
|
3873
|
+
goToActivitiesPage(currentPage - 1, totalPages, itemsPerPage);
|
|
3874
|
+
}
|
|
3875
|
+
});
|
|
3876
|
+
|
|
3877
|
+
prevLi.appendChild(prevA);
|
|
3878
|
+
paginationElement.appendChild(prevLi);
|
|
3879
|
+
|
|
3880
|
+
// Create page number buttons
|
|
3881
|
+
for (let i = 1; i <= totalPages; i++) {
|
|
3882
|
+
const li = document.createElement('li');
|
|
3883
|
+
li.className = `page-item ${i === 1 ? 'active' : ''}`;
|
|
3884
|
+
li.setAttribute('data-page', i);
|
|
3885
|
+
|
|
3886
|
+
const a = document.createElement('a');
|
|
3887
|
+
a.className = 'page-link';
|
|
3888
|
+
a.href = '#';
|
|
3889
|
+
a.textContent = i;
|
|
3890
|
+
a.addEventListener('click', function(e) {
|
|
3891
|
+
e.preventDefault();
|
|
3892
|
+
goToActivitiesPage(i, totalPages, itemsPerPage);
|
|
3893
|
+
});
|
|
3894
|
+
|
|
3895
|
+
li.appendChild(a);
|
|
3896
|
+
paginationElement.appendChild(li);
|
|
3897
|
+
}
|
|
3898
|
+
|
|
3899
|
+
// Create Next button
|
|
3900
|
+
const nextLi = document.createElement('li');
|
|
3901
|
+
nextLi.className = 'page-item';
|
|
3902
|
+
nextLi.id = 'activities-next-page';
|
|
3903
|
+
|
|
3904
|
+
const nextA = document.createElement('a');
|
|
3905
|
+
nextA.className = 'page-link';
|
|
3906
|
+
nextA.href = '#';
|
|
3907
|
+
nextA.innerHTML = '»';
|
|
3908
|
+
nextA.addEventListener('click', function(e) {
|
|
3909
|
+
e.preventDefault();
|
|
3910
|
+
const currentPage = getCurrentActivitiesPage();
|
|
3911
|
+
if (currentPage < totalPages) {
|
|
3912
|
+
goToActivitiesPage(currentPage + 1, totalPages, itemsPerPage);
|
|
3913
|
+
}
|
|
3914
|
+
});
|
|
3915
|
+
|
|
3916
|
+
nextLi.appendChild(nextA);
|
|
3917
|
+
paginationElement.appendChild(nextLi);
|
|
3918
|
+
|
|
3919
|
+
// Show first page and update navigation buttons
|
|
3920
|
+
goToActivitiesPage(1, totalPages, itemsPerPage);
|
|
3921
|
+
}
|
|
3922
|
+
|
|
3923
|
+
// Function to navigate to specific page in Activities table
|
|
3924
|
+
function goToActivitiesPage(pageNumber, totalPages, itemsPerPage) {
|
|
3925
|
+
const container = document.getElementById('activities-container');
|
|
3926
|
+
const paginationElement = document.getElementById('activities-pagination');
|
|
3927
|
+
|
|
3928
|
+
if (!container) return;
|
|
3929
|
+
|
|
3930
|
+
const allRows = Array.from(container.querySelectorAll('.activity-row'));
|
|
3931
|
+
|
|
3932
|
+
// Filter visible rows (considering search)
|
|
3933
|
+
const visibleRows = allRows.filter(row => {
|
|
3934
|
+
const searchVisible = row.getAttribute('data-search-visible');
|
|
3935
|
+
return searchVisible === null || searchVisible === 'true';
|
|
3936
|
+
});
|
|
3937
|
+
|
|
3938
|
+
const startIndex = (pageNumber - 1) * itemsPerPage;
|
|
3939
|
+
const endIndex = startIndex + itemsPerPage;
|
|
3940
|
+
|
|
3941
|
+
console.log('Going to page:', {pageNumber, startIndex, endIndex, totalVisible: visibleRows.length});
|
|
3942
|
+
|
|
3943
|
+
// Hide all rows first
|
|
3944
|
+
allRows.forEach(row => {
|
|
3945
|
+
row.style.display = 'none';
|
|
3946
|
+
});
|
|
3947
|
+
|
|
3948
|
+
// Show rows for current page
|
|
3949
|
+
visibleRows.forEach((row, index) => {
|
|
3950
|
+
if (index >= startIndex && index < endIndex) {
|
|
3951
|
+
row.style.display = '';
|
|
3952
|
+
}
|
|
3953
|
+
});
|
|
3954
|
+
|
|
3955
|
+
// Update pagination active state
|
|
3956
|
+
if (paginationElement) {
|
|
3957
|
+
// Update page number buttons
|
|
3958
|
+
const pageItems = paginationElement.querySelectorAll('.page-item[data-page]');
|
|
3959
|
+
pageItems.forEach((item) => {
|
|
3960
|
+
const page = parseInt(item.getAttribute('data-page'));
|
|
3961
|
+
if (page === pageNumber) {
|
|
3962
|
+
item.classList.add('active');
|
|
3963
|
+
} else {
|
|
3964
|
+
item.classList.remove('active');
|
|
3965
|
+
}
|
|
3966
|
+
});
|
|
3967
|
+
|
|
3968
|
+
// Update Previous button state
|
|
3969
|
+
const prevButton = paginationElement.querySelector('#activities-prev-page');
|
|
3970
|
+
if (prevButton) {
|
|
3971
|
+
if (pageNumber <= 1) {
|
|
3972
|
+
prevButton.classList.add('disabled');
|
|
3973
|
+
} else {
|
|
3974
|
+
prevButton.classList.remove('disabled');
|
|
3975
|
+
}
|
|
3976
|
+
}
|
|
3977
|
+
|
|
3978
|
+
// Update Next button state
|
|
3979
|
+
const nextButton = paginationElement.querySelector('#activities-next-page');
|
|
3980
|
+
if (nextButton) {
|
|
3981
|
+
if (pageNumber >= totalPages) {
|
|
3982
|
+
nextButton.classList.add('disabled');
|
|
3983
|
+
} else {
|
|
3984
|
+
nextButton.classList.remove('disabled');
|
|
3985
|
+
}
|
|
3986
|
+
}
|
|
3987
|
+
}
|
|
3988
|
+
}
|
|
3989
|
+
|
|
3990
|
+
// Function to get current active page in Activities pagination
|
|
3991
|
+
function getCurrentActivitiesPage() {
|
|
3992
|
+
const paginationElement = document.getElementById('activities-pagination');
|
|
3993
|
+
if (!paginationElement) return 1;
|
|
3994
|
+
|
|
3995
|
+
const activeItem = paginationElement.querySelector('.page-item.active[data-page]');
|
|
3996
|
+
if (activeItem) {
|
|
3997
|
+
return parseInt(activeItem.getAttribute('data-page'));
|
|
3998
|
+
}
|
|
3999
|
+
return 1;
|
|
4000
|
+
}
|
|
4001
|
+
|
|
4002
|
+
// Function to scroll to screenshot by ID
|
|
4003
|
+
function scrollToScreenshot(screenshotId) {
|
|
4004
|
+
const screenshotElement = document.getElementById(screenshotId);
|
|
4005
|
+
if (screenshotElement) {
|
|
4006
|
+
// Scroll to the screenshot with smooth behavior
|
|
4007
|
+
screenshotElement.scrollIntoView({
|
|
4008
|
+
behavior: 'smooth',
|
|
4009
|
+
block: 'center'
|
|
4010
|
+
});
|
|
4011
|
+
|
|
4012
|
+
// Add a highlight effect
|
|
4013
|
+
screenshotElement.style.border = '3px solid #007bff';
|
|
4014
|
+
screenshotElement.style.borderRadius = '8px';
|
|
4015
|
+
screenshotElement.style.transition = 'border 0.3s ease';
|
|
4016
|
+
|
|
4017
|
+
// Remove highlight after 3 seconds
|
|
4018
|
+
setTimeout(() => {
|
|
4019
|
+
screenshotElement.style.border = '';
|
|
4020
|
+
screenshotElement.style.borderRadius = '';
|
|
4021
|
+
}, 3000);
|
|
4022
|
+
} else {
|
|
4023
|
+
console.warn('Screenshot with ID', screenshotId, 'not found');
|
|
4024
|
+
}
|
|
4025
|
+
}
|
|
4026
|
+
</script>
|
|
4027
|
+
</body>
|
|
4028
|
+
</html>
|