robotframework-dashboard 2.2.0__tar.gz → 2.3.0__tar.gz
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.
- {robotframework_dashboard-2.2.0/robotframework_dashboard.egg-info → robotframework_dashboard-2.3.0}/PKG-INFO +4 -3
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/README.md +3 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/arguments.py +2 -3
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/css/base.css +0 -2
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/css/components.css +39 -5
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/dashboard.py +7 -1
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/database.py +53 -4
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/bootstrap.css +6 -0
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/bootstrap.js +7 -0
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/chart.js +14 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/dependencies/chartjs-adapter-date-fns.js +1 -1
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/chartjs-chart-boxplot.js +2 -0
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/chartjs-chart-matrix.js +8 -0
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/chartjs-plugin-datalabels.js +7 -0
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/datatables.css +21 -0
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/datatables.js +22 -0
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/gridstack.css +1 -0
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/gridstack.js +9 -0
- robotframework_dashboard-2.3.0/robotframework_dashboard/dependencies/pako.js +2 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/dependencies.py +29 -25
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/admin_page/admin_api.js +21 -3
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/admin_page/admin_eventlisteners.js +0 -1
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/admin_page/admin_information.js +1 -1
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/common.js +24 -15
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/customsections.js +0 -9
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/eventlisteners.js +50 -62
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/filter.js +52 -157
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_creation/all.js +14 -3
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_creation/chart_factory.js +0 -2
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_creation/compare.js +2 -4
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_creation/config_helpers.js +4 -5
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_creation/keyword.js +84 -4
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_creation/overview.js +85 -224
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_creation/run.js +0 -3
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_creation/suite.js +0 -4
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_creation/tables.js +19 -6
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_creation/test.js +3 -4
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/duration.js +0 -2
- robotframework_dashboard-2.3.0/robotframework_dashboard/js/graph_data/exceptions.js +94 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/failed.js +7 -2
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/flaky.js +30 -49
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/graph_config.js +6 -1
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/helpers.js +80 -10
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/messages.js +6 -2
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/statistics.js +31 -17
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/stats.js +6 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/tooltip_helpers.js +0 -1
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/information.js +1 -1
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/layout.js +8 -27
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/linkwidgets.js +0 -15
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/localstorage.js +3 -9
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/log.js +1 -1
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/menu.js +10 -40
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/statwidgets.js +0 -25
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/theme.js +0 -9
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/variables/chartconfig.js +5 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/variables/data.js +3 -1
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/variables/globals.js +2 -2
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/variables/graphmetadata.js +17 -7
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/variables/information.js +9 -5
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/variables/settings.js +3 -3
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/variables/statwidgetdefs.js +3 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/licenses/THIRD_PARTY_LICENSES.txt +65 -56
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/processors.py +114 -11
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/queries.py +7 -2
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/robotdashboardlistener.py +0 -6
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/server.py +2 -10
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/templates/admin.html +1 -1
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/templates/dashboard.html +44 -17
- robotframework_dashboard-2.3.0/robotframework_dashboard/version.py +1 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0/robotframework_dashboard.egg-info}/PKG-INFO +4 -3
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard.egg-info/SOURCES.txt +1 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/setup.py +4 -3
- robotframework_dashboard-2.2.0/robotframework_dashboard/dependencies/bootstrap.css +0 -6
- robotframework_dashboard-2.2.0/robotframework_dashboard/dependencies/bootstrap.js +0 -7
- robotframework_dashboard-2.2.0/robotframework_dashboard/dependencies/chart.js +0 -20
- robotframework_dashboard-2.2.0/robotframework_dashboard/dependencies/chartjs-chart-boxplot.js +0 -2
- robotframework_dashboard-2.2.0/robotframework_dashboard/dependencies/chartjs-chart-matrix.js +0 -8
- robotframework_dashboard-2.2.0/robotframework_dashboard/dependencies/chartjs-plugin-datalabels.js +0 -7
- robotframework_dashboard-2.2.0/robotframework_dashboard/dependencies/datatables.css +0 -18
- robotframework_dashboard-2.2.0/robotframework_dashboard/dependencies/datatables.js +0 -25
- robotframework_dashboard-2.2.0/robotframework_dashboard/dependencies/gridstack.css +0 -1
- robotframework_dashboard-2.2.0/robotframework_dashboard/dependencies/gridstack.js +0 -9
- robotframework_dashboard-2.2.0/robotframework_dashboard/dependencies/pako.js +0 -2
- robotframework_dashboard-2.2.0/robotframework_dashboard/version.py +0 -1
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/LICENSE +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/MANIFEST.in +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/pyproject.toml +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/__init__.py +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/abstractdb.py +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/css/colors.css +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/css/dark.css +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/admin_page/admin_common.js +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/admin_page/admin_main.js +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/admin_page/admin_theme.js +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/database.js +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/donut.js +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/duration_deviation.js +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/heatmap.js +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/graph_data/time_consuming.js +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/main.js +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/variables/graphs.js +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/js/variables/svg.js +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/main.py +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard/robotdashboard.py +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard.egg-info/dependency_links.txt +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard.egg-info/entry_points.txt +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard.egg-info/not-zip-safe +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard.egg-info/requires.txt +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/robotframework_dashboard.egg-info/top_level.txt +0 -0
- {robotframework_dashboard-2.2.0 → robotframework_dashboard-2.3.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: robotframework-dashboard
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: Output processor and dashboard generator for Robot Framework output files
|
|
5
5
|
Home-page: https://github.com/marketsquare/robotframework-dashboard
|
|
6
6
|
Author: Tim de Groot
|
|
@@ -90,8 +90,9 @@ For all available CLI options see the [Basic CLI docs](https://marketsquare.gith
|
|
|
90
90
|
- 🛠️ **Custom Database Classes** - Extend or replace the database processor for custom backends.
|
|
91
91
|
- 🖥️ **Server Mode** - Host your dashboard for multi-user access and automatic updates.
|
|
92
92
|
- 🎧 **Listener Integration** - Automatically updates dashboard after every test run.
|
|
93
|
-
- 📝 **Message Config Support** - Group similar test failures using regex-based patterns.
|
|
94
|
-
-
|
|
93
|
+
- 📝 **Message Config Support** - Group similar test failures using regex-based patterns.
|
|
94
|
+
- 🚨 **Exception Tracking** - Track exception messages caught by TRY/EXCEPT blocks across runs, with a dedicated graph and table.
|
|
95
|
+
- ⚙️ **Configurable Defaults** - Preload dashboard settings via JSON for consistent appearance.
|
|
95
96
|
|
|
96
97
|
…and many more advanced features to help you visualize, analyze, and manage your Robot Framework test results with ease!
|
|
97
98
|
|
|
@@ -57,10 +57,12 @@ For all available CLI options see the [Basic CLI docs](https://marketsquare.gith
|
|
|
57
57
|
- 🔎 **Global Filters** - Filter runs by name, tags, date, metadata, or quantity.
|
|
58
58
|
- ⚖️ **Comparison Mode** - Compare up to 4 runs side by side with visual statistics.
|
|
59
59
|
- 🔗 **Automatic Log Linking** - Open Robot Framework logs directly from the dashboard.
|
|
60
|
+
- 🔁 **Rerun History** - Merged `--rerunfailed` outputs keep the status of every attempt per test.
|
|
60
61
|
- 🛠️ **Custom Database Classes** - Extend or replace the database processor for custom backends.
|
|
61
62
|
- 🖥️ **Server Mode** - Host your dashboard for multi-user access and automatic updates.
|
|
62
63
|
- 🎧 **Listener Integration** - Automatically updates dashboard after every test run.
|
|
63
64
|
- 📝 **Message Config Support** - Group similar test failures using regex-based patterns.
|
|
65
|
+
- 🚨 **Exception Tracking** - Track exception messages caught by TRY/EXCEPT blocks across runs, with a dedicated graph and table.
|
|
64
66
|
- ⚙️ **Configurable Defaults** - Preload dashboard settings via JSON for consistent appearance.
|
|
65
67
|
|
|
66
68
|
…and many more advanced features to help you visualize, analyze, and manage your Robot Framework test results with ease!
|
|
@@ -83,6 +85,7 @@ For detailed usage instructions, advanced examples, and full documentation, visi
|
|
|
83
85
|
- 🗄️ [**Custom Database Class**](https://marketsquare.github.io/robotframework-dashboard/custom-database-class.html) - Extend or replace the default database backend to suit your storage needs, including SQLite, MySQL, or custom implementations.
|
|
84
86
|
- 🔔 [**Listener Integration**](https://marketsquare.github.io/robotframework-dashboard/listener-integration.html) - Use a listener to automatically push test results to the dashboard for every executed run, integrating seamlessly into CI/CD pipelines.
|
|
85
87
|
- 📂 [**Log Linking**](https://marketsquare.github.io/robotframework-dashboard/log-linking.html) - Enable clickable log navigation from dashboard graphs, covering file naming conventions, local and server usage, and remote log uploads.
|
|
88
|
+
- 🔁 [**Reruns**](https://marketsquare.github.io/robotframework-dashboard/reruns.html) - Feed merged `--rerunfailed` outputs to the dashboard and see the attempt history of every re-executed test.
|
|
86
89
|
- 📈 [**Performance**](https://marketsquare.github.io/robotframework-dashboard/performance.html) - Performance results across different workload sizes, guidance on scaling to hundreds of runs, and tips for large result sets.
|
|
87
90
|
|
|
88
91
|
|
|
@@ -78,9 +78,8 @@ class ArgumentParser:
|
|
|
78
78
|
lets users write the combo with the regular comma separator (e.g.
|
|
79
79
|
'-r "limit=10,tag=nightly"') instead of ';'.
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
just runs as two independent operations, same as on main.
|
|
81
|
+
Only 'limit' can be scoped by tags; 'age' + 'tags' run as two
|
|
82
|
+
independent operations.
|
|
84
83
|
|
|
85
84
|
Rules:
|
|
86
85
|
- No tags, or tags without a 'limit' partner -> no combination, every
|
|
@@ -213,7 +213,6 @@ body.lock-scroll {
|
|
|
213
213
|
cursor: pointer;
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
/* ── Info label + small icon (tooltip on label hover) ── */
|
|
217
216
|
.info-label {
|
|
218
217
|
display: inline-flex !important;
|
|
219
218
|
align-items: center;
|
|
@@ -283,7 +282,6 @@ body.lock-scroll {
|
|
|
283
282
|
display: block;
|
|
284
283
|
}
|
|
285
284
|
|
|
286
|
-
/* ── Sidebar (off-canvas menu for overflowed items) ── */
|
|
287
285
|
.sidenav-backdrop {
|
|
288
286
|
position: fixed;
|
|
289
287
|
top: 0; left: 0; right: 0; bottom: 0;
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
flex-wrap: wrap;
|
|
38
38
|
align-items: center;
|
|
39
39
|
column-gap: 0.75rem;
|
|
40
|
-
row-gap:
|
|
40
|
+
row-gap: 6px;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
.section-filters .filter-group {
|
|
@@ -115,6 +115,37 @@
|
|
|
115
115
|
display: block;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
.filter-search-wrapper {
|
|
119
|
+
position: relative;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.filter-search-wrapper .form-control {
|
|
123
|
+
padding-right: 1.5rem;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.filter-search-clear {
|
|
127
|
+
position: absolute;
|
|
128
|
+
top: 0;
|
|
129
|
+
right: 0;
|
|
130
|
+
bottom: 0;
|
|
131
|
+
width: 1.5rem;
|
|
132
|
+
display: flex;
|
|
133
|
+
align-items: center;
|
|
134
|
+
justify-content: center;
|
|
135
|
+
padding: 0 0 0.15rem 0;
|
|
136
|
+
border: none;
|
|
137
|
+
background: none;
|
|
138
|
+
color: inherit;
|
|
139
|
+
opacity: 0.6;
|
|
140
|
+
font-size: 1.4rem;
|
|
141
|
+
line-height: 1;
|
|
142
|
+
cursor: pointer;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.filter-search-clear:hover {
|
|
146
|
+
opacity: 1;
|
|
147
|
+
}
|
|
148
|
+
|
|
118
149
|
.filter-profile-bar {
|
|
119
150
|
border-bottom: 1px solid var(--bs-border-color);
|
|
120
151
|
}
|
|
@@ -295,8 +326,6 @@
|
|
|
295
326
|
.grid-stack-item-content.red-bg { background-color: rgba(206, 62, 1, 0.2); }
|
|
296
327
|
.grid-stack-item-content.yellow-bg { background-color: rgba(254, 216, 79, 0.2); }
|
|
297
328
|
|
|
298
|
-
/* ─── Custom link widget ─────────────────────────────────────────────────── */
|
|
299
|
-
|
|
300
329
|
/* Inner layout — mirrors stat-widget-inner */
|
|
301
330
|
.link-widget-inner {
|
|
302
331
|
position: relative;
|
|
@@ -379,8 +408,6 @@
|
|
|
379
408
|
margin-right: 96px;
|
|
380
409
|
}
|
|
381
410
|
|
|
382
|
-
/* ─────────────────────────────────────────────────────────────────────────── */
|
|
383
|
-
|
|
384
411
|
.fullscreen .stat-value {
|
|
385
412
|
font-size: 4rem;
|
|
386
413
|
}
|
|
@@ -557,6 +584,13 @@ canvas {
|
|
|
557
584
|
filter: brightness(0.5);
|
|
558
585
|
}
|
|
559
586
|
|
|
587
|
+
/* Bootstrap >= 5.3.1 no longer spaces the modal header itself (only its .btn-close gets
|
|
588
|
+
margin-left: auto); the dashboard modals use their own button groups, so keep the
|
|
589
|
+
title left and the buttons right */
|
|
590
|
+
.modal-header {
|
|
591
|
+
justify-content: space-between;
|
|
592
|
+
}
|
|
593
|
+
|
|
560
594
|
#runTag {
|
|
561
595
|
max-height: 400px;
|
|
562
596
|
overflow: auto;
|
|
@@ -68,6 +68,10 @@ class DashboardGenerator:
|
|
|
68
68
|
'"placeholder_keywords"',
|
|
69
69
|
f'"{self._compress_and_encode(data["keywords"])}"',
|
|
70
70
|
)
|
|
71
|
+
dashboard_data = dashboard_data.replace(
|
|
72
|
+
'"placeholder_exceptions"',
|
|
73
|
+
f'"{self._compress_and_encode(data.get("exceptions", []))}"',
|
|
74
|
+
)
|
|
71
75
|
dashboard_data = dashboard_data.replace(
|
|
72
76
|
'"placeholder_amount"', str(quantity)
|
|
73
77
|
)
|
|
@@ -93,9 +97,11 @@ class DashboardGenerator:
|
|
|
93
97
|
f"Robot Framework Dashboard - {str(generation_datetime)[:-7]}",
|
|
94
98
|
)
|
|
95
99
|
if message_config:
|
|
100
|
+
# the placeholder sits inside a single-quoted JS string literal that is
|
|
101
|
+
# JSON.parse'd at runtime, so escape backslashes and single quotes
|
|
96
102
|
dashboard_data = dashboard_data.replace(
|
|
97
103
|
'"placeholder_message_config"',
|
|
98
|
-
|
|
104
|
+
dumps(message_config).replace("\\", "\\\\").replace("'", "\\'"),
|
|
99
105
|
)
|
|
100
106
|
if json_config:
|
|
101
107
|
dashboard_data = dashboard_data.replace(
|
|
@@ -75,6 +75,7 @@ class DatabaseProcessor(AbstractDatabaseProcessor):
|
|
|
75
75
|
# run: metadata was added in 1.0.0
|
|
76
76
|
# keyword: owner was added in 1.2.0
|
|
77
77
|
# run: project_version was added in 1.3.0
|
|
78
|
+
# test: attempts (rebot --merge rerun history) was added in 2.3.0
|
|
78
79
|
run_table_length = get_runs_length()
|
|
79
80
|
if run_table_length == 10: # -> column alias not present
|
|
80
81
|
self.connection.cursor().execute(RUN_TABLE_UPDATE_ALIAS)
|
|
@@ -119,6 +120,10 @@ class DatabaseProcessor(AbstractDatabaseProcessor):
|
|
|
119
120
|
self.connection.cursor().execute(TEST_TABLE_UPDATE_ID)
|
|
120
121
|
self.connection.commit()
|
|
121
122
|
test_table_length = get_tests_length()
|
|
123
|
+
if test_table_length == 12:
|
|
124
|
+
self.connection.cursor().execute(TEST_TABLE_UPDATE_ATTEMPTS)
|
|
125
|
+
self.connection.commit()
|
|
126
|
+
test_table_length = get_tests_length()
|
|
122
127
|
|
|
123
128
|
keyword_table_length = get_keywords_length()
|
|
124
129
|
if keyword_table_length == 10:
|
|
@@ -129,11 +134,15 @@ class DatabaseProcessor(AbstractDatabaseProcessor):
|
|
|
129
134
|
self.connection.cursor().execute(KEYWORD_TABLE_UPDATE_OWNER)
|
|
130
135
|
self.connection.commit()
|
|
131
136
|
keyword_table_length = get_keywords_length()
|
|
137
|
+
# exceptions table: added later, safe to create if missing
|
|
138
|
+
self.connection.cursor().execute(CREATE_EXCEPTIONS)
|
|
139
|
+
self.connection.commit()
|
|
132
140
|
else:
|
|
133
141
|
self.connection.cursor().execute(CREATE_RUNS)
|
|
134
142
|
self.connection.cursor().execute(CREATE_SUITES)
|
|
135
143
|
self.connection.cursor().execute(CREATE_TESTS)
|
|
136
144
|
self.connection.cursor().execute(CREATE_KEYWORDS)
|
|
145
|
+
self.connection.cursor().execute(CREATE_EXCEPTIONS)
|
|
137
146
|
self.connection.commit()
|
|
138
147
|
|
|
139
148
|
def close_database(self):
|
|
@@ -159,6 +168,7 @@ class DatabaseProcessor(AbstractDatabaseProcessor):
|
|
|
159
168
|
self._insert_suites(output_data["suites"], run_alias, timezone)
|
|
160
169
|
self._insert_tests(output_data["tests"], run_alias, timezone)
|
|
161
170
|
self._insert_keywords(output_data["keywords"], run_alias, timezone)
|
|
171
|
+
self._insert_exceptions(output_data.get("exceptions", []), run_alias, timezone)
|
|
162
172
|
except Exception as error:
|
|
163
173
|
print(f" ERROR: something went wrong with the database: {error}")
|
|
164
174
|
|
|
@@ -227,6 +237,18 @@ class DatabaseProcessor(AbstractDatabaseProcessor):
|
|
|
227
237
|
self.connection.executemany(INSERT_INTO_KEYWORDS, full_keywords)
|
|
228
238
|
self.connection.commit()
|
|
229
239
|
|
|
240
|
+
def _insert_exceptions(self, exceptions: list, run_alias: str, timezone: str = ""):
|
|
241
|
+
"""Helper function to insert the exception data"""
|
|
242
|
+
full_exceptions = []
|
|
243
|
+
for exc in exceptions:
|
|
244
|
+
exc = list(exc)
|
|
245
|
+
if timezone:
|
|
246
|
+
exc[0] = f"{exc[0]}{timezone}"
|
|
247
|
+
exc.append(run_alias)
|
|
248
|
+
full_exceptions.append(tuple(exc))
|
|
249
|
+
self.connection.executemany(INSERT_INTO_EXCEPTIONS, full_exceptions)
|
|
250
|
+
self.connection.commit()
|
|
251
|
+
|
|
230
252
|
@staticmethod
|
|
231
253
|
def _get_local_timezone_offset():
|
|
232
254
|
"""Helper function to get the local machine's timezone offset as a string like +01:00"""
|
|
@@ -250,7 +272,7 @@ class DatabaseProcessor(AbstractDatabaseProcessor):
|
|
|
250
272
|
|
|
251
273
|
def get_data(self):
|
|
252
274
|
"""This function gets all the data in the database"""
|
|
253
|
-
data, runs, suites, tests, keywords, aliases = {}, [], [], [], [], {}
|
|
275
|
+
data, runs, suites, tests, keywords, exceptions, aliases = {}, [], [], [], [], [], {}
|
|
254
276
|
name_labels = {}
|
|
255
277
|
local_tz = self._get_local_timezone_offset()
|
|
256
278
|
alias_counter = 1
|
|
@@ -327,6 +349,8 @@ class DatabaseProcessor(AbstractDatabaseProcessor):
|
|
|
327
349
|
row["tags"] = ""
|
|
328
350
|
if row["id"] == None:
|
|
329
351
|
row["id"] == ""
|
|
352
|
+
if row.get("attempts") == None:
|
|
353
|
+
row["attempts"] = ""
|
|
330
354
|
# For older entries without timezone in run_start, append current local timezone
|
|
331
355
|
if not self._has_timezone_offset(row["run_start"]):
|
|
332
356
|
row["run_start"] = f"{row['run_start']}{local_tz}"
|
|
@@ -349,6 +373,21 @@ class DatabaseProcessor(AbstractDatabaseProcessor):
|
|
|
349
373
|
name_prefix_lookup.get(row["run_start"][:19], ""))
|
|
350
374
|
keywords.append(row)
|
|
351
375
|
data["keywords"] = keywords
|
|
376
|
+
# Get exceptions from exceptions table
|
|
377
|
+
try:
|
|
378
|
+
exception_rows = self.connection.cursor().execute(SELECT_FROM_EXCEPTIONS).fetchall()
|
|
379
|
+
for exception_row in exception_rows:
|
|
380
|
+
row = self._dict_from_row(exception_row)
|
|
381
|
+
if not self._has_timezone_offset(row["run_start"]):
|
|
382
|
+
row["run_start"] = f"{row['run_start']}{local_tz}"
|
|
383
|
+
row["run_alias"] = aliases.get(row["run_start"],
|
|
384
|
+
alias_prefix_lookup.get(row["run_start"][:19], ""))
|
|
385
|
+
row["run_name"] = name_labels.get(row["run_start"],
|
|
386
|
+
name_prefix_lookup.get(row["run_start"][:19], ""))
|
|
387
|
+
exceptions.append(row)
|
|
388
|
+
except Exception:
|
|
389
|
+
pass # table may not exist in older/custom databases
|
|
390
|
+
data["exceptions"] = exceptions
|
|
352
391
|
return data
|
|
353
392
|
|
|
354
393
|
def _dict_from_row(self, row: sqlite3.Row):
|
|
@@ -527,6 +566,15 @@ class DatabaseProcessor(AbstractDatabaseProcessor):
|
|
|
527
566
|
console = ""
|
|
528
567
|
parts = run.split(";")
|
|
529
568
|
limit = int(parts[0].replace("limit=", ""))
|
|
569
|
+
# A limit below 1 would slice past the start of the candidate list and
|
|
570
|
+
# remove every run (issue #333); removing everything is a separate,
|
|
571
|
+
# explicit action.
|
|
572
|
+
if limit < 1:
|
|
573
|
+
print(
|
|
574
|
+
f" ERROR: no runs were removed as the provided limit ({limit}) must be at least 1"
|
|
575
|
+
)
|
|
576
|
+
console += f" ERROR: no runs were removed as the provided limit ({limit}) must be at least 1\n"
|
|
577
|
+
return console
|
|
530
578
|
tag_filters = [
|
|
531
579
|
part.replace("tag=", "") for part in parts[1:] if part.startswith("tag=")
|
|
532
580
|
]
|
|
@@ -557,9 +605,6 @@ class DatabaseProcessor(AbstractDatabaseProcessor):
|
|
|
557
605
|
return console
|
|
558
606
|
|
|
559
607
|
def _remove_by_age(self, run_query: str, run_starts: list):
|
|
560
|
-
# NOTE: issue #309 / PR #313 only asked for tag-scoped retention on
|
|
561
|
-
# "limit" (see _remove_by_limit); age intentionally has no tag scoping
|
|
562
|
-
# here, matching the original issue's proposed direction.
|
|
563
608
|
console = ""
|
|
564
609
|
try:
|
|
565
610
|
clean_query = run_query.replace("age=", "")
|
|
@@ -604,6 +649,10 @@ class DatabaseProcessor(AbstractDatabaseProcessor):
|
|
|
604
649
|
cursor.execute(DELETE_FROM_SUITES.format(run_start=run_start))
|
|
605
650
|
cursor.execute(DELETE_FROM_TESTS.format(run_start=run_start))
|
|
606
651
|
cursor.execute(DELETE_FROM_KEYWORDS.format(run_start=run_start))
|
|
652
|
+
try:
|
|
653
|
+
cursor.execute(DELETE_FROM_EXCEPTIONS.format(run_start=run_start))
|
|
654
|
+
except Exception:
|
|
655
|
+
pass # table may not exist in older/custom databases
|
|
607
656
|
# Log inside the transaction: if the write fails, the transaction
|
|
608
657
|
# rolls back and the run is not deleted.
|
|
609
658
|
if self.log_removed_path and entry:
|