orionis 0.560.0__py3-none-any.whl → 0.562.0__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.
- orionis/console/stub/command.stub +23 -0
- orionis/console/stub/listener.stub +167 -0
- orionis/metadata/framework.py +1 -1
- orionis/test/view/report.stub +565 -0
- {orionis-0.560.0.dist-info → orionis-0.562.0.dist-info}/METADATA +1 -1
- {orionis-0.560.0.dist-info → orionis-0.562.0.dist-info}/RECORD +9 -6
- {orionis-0.560.0.dist-info → orionis-0.562.0.dist-info}/WHEEL +0 -0
- {orionis-0.560.0.dist-info → orionis-0.562.0.dist-info}/licenses/LICENCE +0 -0
- {orionis-0.560.0.dist-info → orionis-0.562.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
from orionis.console.args.argument import CLIArgument
|
2
|
+
from orionis.console.base.command import BaseCommand
|
3
|
+
|
4
|
+
class {{command-class}}(BaseCommand):
|
5
|
+
|
6
|
+
# Command executed from the CLI, by convention starts with "app:".
|
7
|
+
signature: str = "{{command-signature}}"
|
8
|
+
|
9
|
+
# Brief description of the command.
|
10
|
+
description: str = "{{command-description}}"
|
11
|
+
|
12
|
+
# List of command arguments and options.
|
13
|
+
arguments: list[CLIArgument] = [
|
14
|
+
# Example:
|
15
|
+
# CLIArgument("--name", type=str, help="Example name", required=True)
|
16
|
+
]
|
17
|
+
|
18
|
+
async def handle(self) -> None:
|
19
|
+
"""
|
20
|
+
Main logic of the command.
|
21
|
+
Override this method to implement the desired behavior.
|
22
|
+
"""
|
23
|
+
pass
|
@@ -0,0 +1,167 @@
|
|
1
|
+
from orionis.console.base.scheduler_event_listener import BaseScheduleEventListener
|
2
|
+
from orionis.console.contracts.schedule import ISchedule
|
3
|
+
from orionis.console.entities.event_job import EventJob
|
4
|
+
|
5
|
+
class {{name-listener}}(BaseScheduleEventListener):
|
6
|
+
|
7
|
+
async def before(self, event: EventJob, schedule: ISchedule) -> None:
|
8
|
+
"""
|
9
|
+
Called before processing a job submission event.
|
10
|
+
|
11
|
+
Parameters
|
12
|
+
----------
|
13
|
+
event : EventJob
|
14
|
+
The job submission event containing details about the job.
|
15
|
+
schedule : ISchedule
|
16
|
+
The associated schedule instance managing the job.
|
17
|
+
|
18
|
+
Returns
|
19
|
+
-------
|
20
|
+
None
|
21
|
+
This method does not return any value.
|
22
|
+
"""
|
23
|
+
pass
|
24
|
+
|
25
|
+
async def after(self, event: EventJob, schedule: ISchedule) -> None:
|
26
|
+
"""
|
27
|
+
Called after processing a job execution event.
|
28
|
+
|
29
|
+
Parameters
|
30
|
+
----------
|
31
|
+
event : EventJob
|
32
|
+
The job execution event containing details about the executed job.
|
33
|
+
schedule : ISchedule
|
34
|
+
The associated schedule instance managing the job.
|
35
|
+
|
36
|
+
Returns
|
37
|
+
-------
|
38
|
+
None
|
39
|
+
This method does not return any value.
|
40
|
+
"""
|
41
|
+
pass
|
42
|
+
|
43
|
+
async def onSuccess(self, event: EventJob, schedule: ISchedule) -> None:
|
44
|
+
"""
|
45
|
+
Called when a job is successfully executed.
|
46
|
+
|
47
|
+
Parameters
|
48
|
+
----------
|
49
|
+
event : EventJob
|
50
|
+
The event containing details about the successful job execution.
|
51
|
+
schedule : ISchedule
|
52
|
+
The associated schedule instance managing the job.
|
53
|
+
|
54
|
+
Returns
|
55
|
+
-------
|
56
|
+
None
|
57
|
+
This method does not return any value.
|
58
|
+
"""
|
59
|
+
pass
|
60
|
+
|
61
|
+
async def onFailure(self, event: EventJob, schedule: ISchedule) -> None:
|
62
|
+
"""
|
63
|
+
Called when a job execution fails.
|
64
|
+
|
65
|
+
Parameters
|
66
|
+
----------
|
67
|
+
event : EventJob
|
68
|
+
The event containing details about the job execution failure.
|
69
|
+
schedule : ISchedule
|
70
|
+
The associated schedule instance managing the job.
|
71
|
+
|
72
|
+
Returns
|
73
|
+
-------
|
74
|
+
None
|
75
|
+
This method does not return any value.
|
76
|
+
"""
|
77
|
+
pass
|
78
|
+
|
79
|
+
async def onMissed(self, event: EventJob, schedule: ISchedule) -> None:
|
80
|
+
"""
|
81
|
+
Called when a job execution is missed.
|
82
|
+
|
83
|
+
Parameters
|
84
|
+
----------
|
85
|
+
event : EventJob
|
86
|
+
The event containing details about the missed job execution.
|
87
|
+
schedule : ISchedule
|
88
|
+
The associated schedule instance managing the job.
|
89
|
+
|
90
|
+
Returns
|
91
|
+
-------
|
92
|
+
None
|
93
|
+
This method does not return any value.
|
94
|
+
"""
|
95
|
+
pass
|
96
|
+
|
97
|
+
async def onMaxInstances(self, event: EventJob, schedule: ISchedule) -> None:
|
98
|
+
"""
|
99
|
+
Called when a job exceeds the maximum allowed instances.
|
100
|
+
|
101
|
+
Parameters
|
102
|
+
----------
|
103
|
+
event : EventJob
|
104
|
+
The event containing details about the max instances violation.
|
105
|
+
schedule : ISchedule
|
106
|
+
The associated schedule instance managing the job.
|
107
|
+
|
108
|
+
Returns
|
109
|
+
-------
|
110
|
+
None
|
111
|
+
This method does not return any value.
|
112
|
+
"""
|
113
|
+
pass
|
114
|
+
|
115
|
+
async def onPaused(self, event: EventJob, schedule: ISchedule) -> None:
|
116
|
+
"""
|
117
|
+
Called when the scheduler is paused.
|
118
|
+
|
119
|
+
Parameters
|
120
|
+
----------
|
121
|
+
event : EventJob
|
122
|
+
The event containing details about the scheduler pause.
|
123
|
+
schedule : ISchedule
|
124
|
+
The associated schedule instance managing the jobs.
|
125
|
+
|
126
|
+
Returns
|
127
|
+
-------
|
128
|
+
None
|
129
|
+
This method does not return any value.
|
130
|
+
"""
|
131
|
+
pass
|
132
|
+
|
133
|
+
async def onResumed(self, event: EventJob, schedule: ISchedule) -> None:
|
134
|
+
"""
|
135
|
+
Called when the scheduler is resumed.
|
136
|
+
|
137
|
+
Parameters
|
138
|
+
----------
|
139
|
+
event : EventJob
|
140
|
+
The event containing details about the scheduler resume.
|
141
|
+
schedule : ISchedule
|
142
|
+
The associated schedule instance managing the jobs.
|
143
|
+
|
144
|
+
Returns
|
145
|
+
-------
|
146
|
+
None
|
147
|
+
This method does not return any value.
|
148
|
+
"""
|
149
|
+
pass
|
150
|
+
|
151
|
+
async def onRemoved(self, event: EventJob, schedule: ISchedule) -> None:
|
152
|
+
"""
|
153
|
+
Called when a job is removed from the scheduler.
|
154
|
+
|
155
|
+
Parameters
|
156
|
+
----------
|
157
|
+
event : EventJob
|
158
|
+
The event containing details about the job removal.
|
159
|
+
schedule : ISchedule
|
160
|
+
The associated schedule instance managing the jobs.
|
161
|
+
|
162
|
+
Returns
|
163
|
+
-------
|
164
|
+
None
|
165
|
+
This method does not return any value.
|
166
|
+
"""
|
167
|
+
pass
|
orionis/metadata/framework.py
CHANGED
@@ -0,0 +1,565 @@
|
|
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>Orionis Test Dashboard</title>
|
7
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
8
|
+
<link rel="icon" href="https://orionis-framework.com/svg/logo.svg" />
|
9
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
10
|
+
<link href="https://unpkg.com/gridjs/dist/theme/mermaid.min.css" rel="stylesheet" />
|
11
|
+
<link href="https://fonts.googleapis.com/css2?family=Fira+Mono:wght@400;500;700&display=swap" rel="stylesheet">
|
12
|
+
<style>
|
13
|
+
#test-table,
|
14
|
+
#test-table .gridjs-td,
|
15
|
+
#test-table .gridjs-th {
|
16
|
+
font-family: 'Fira Mono', 'Consolas', 'Menlo', 'Monaco', 'Liberation Mono', monospace !important;
|
17
|
+
}
|
18
|
+
|
19
|
+
#test-table .gridjs-td,
|
20
|
+
#test-table .gridjs-th {
|
21
|
+
max-width: 220px;
|
22
|
+
white-space: normal;
|
23
|
+
overflow-wrap: anywhere;
|
24
|
+
text-overflow: initial;
|
25
|
+
vertical-align: middle;
|
26
|
+
font-size: 0.85rem;
|
27
|
+
line-height: 1.28;
|
28
|
+
}
|
29
|
+
|
30
|
+
#test-table .gridjs-th {
|
31
|
+
font-size: 0.82rem !important;
|
32
|
+
font-weight: 600;
|
33
|
+
letter-spacing: 0.01em;
|
34
|
+
}
|
35
|
+
|
36
|
+
#test-table .gridjs-td.status-cell {
|
37
|
+
max-width: 90px;
|
38
|
+
text-align: center;
|
39
|
+
font-weight: 600;
|
40
|
+
letter-spacing: 0.02em;
|
41
|
+
white-space: nowrap;
|
42
|
+
font-size: 0.75em;
|
43
|
+
padding-top: 0.2em;
|
44
|
+
padding-bottom: 0.2em;
|
45
|
+
}
|
46
|
+
|
47
|
+
#test-table .gridjs-td.doc-cell {
|
48
|
+
max-width: 60px;
|
49
|
+
text-align: center;
|
50
|
+
white-space: nowrap;
|
51
|
+
}
|
52
|
+
|
53
|
+
#test-table {
|
54
|
+
overflow-x: auto;
|
55
|
+
}
|
56
|
+
|
57
|
+
.badge-status {
|
58
|
+
display: inline-flex;
|
59
|
+
align-items: center;
|
60
|
+
gap: 0.3em;
|
61
|
+
padding: 0.13em 0.6em;
|
62
|
+
border-radius: 9999px;
|
63
|
+
font-size: 0.78em;
|
64
|
+
font-weight: 600;
|
65
|
+
letter-spacing: 0.01em;
|
66
|
+
}
|
67
|
+
|
68
|
+
.badge-passed {
|
69
|
+
background: #d1fae5;
|
70
|
+
color: #059669;
|
71
|
+
}
|
72
|
+
|
73
|
+
.badge-failed {
|
74
|
+
background: #fee2e2;
|
75
|
+
color: #dc2626;
|
76
|
+
}
|
77
|
+
|
78
|
+
.badge-errors {
|
79
|
+
background: #fef9c3;
|
80
|
+
color: #ca8a04;
|
81
|
+
}
|
82
|
+
|
83
|
+
.badge-skipped {
|
84
|
+
background: #e0e7ff;
|
85
|
+
color: #3730a3;
|
86
|
+
}
|
87
|
+
|
88
|
+
.doc-btn {
|
89
|
+
background: #eef2ff;
|
90
|
+
color: #3730a3;
|
91
|
+
border: none;
|
92
|
+
border-radius: 0.5em;
|
93
|
+
padding: 0.2em 0.8em;
|
94
|
+
font-size: 0.92em;
|
95
|
+
font-weight: 500;
|
96
|
+
cursor: pointer;
|
97
|
+
transition: background 0.15s;
|
98
|
+
font-family: inherit;
|
99
|
+
}
|
100
|
+
|
101
|
+
.doc-btn:hover {
|
102
|
+
background: #c7d2fe;
|
103
|
+
}
|
104
|
+
|
105
|
+
/* Modal styles */
|
106
|
+
.orionis-modal {
|
107
|
+
position: fixed;
|
108
|
+
left: 0;
|
109
|
+
top: 0;
|
110
|
+
width: 100vw;
|
111
|
+
height: 100vh;
|
112
|
+
background: rgba(30, 41, 59, 0.45);
|
113
|
+
display: flex !important;
|
114
|
+
align-items: center;
|
115
|
+
justify-content: center;
|
116
|
+
z-index: 10000;
|
117
|
+
}
|
118
|
+
|
119
|
+
.orionis-modal-content {
|
120
|
+
background: white;
|
121
|
+
max-width: 70vw;
|
122
|
+
width: 70vw;
|
123
|
+
padding: 2em 1.5em 1.2em 1.5em;
|
124
|
+
border-radius: 1.2em;
|
125
|
+
box-shadow: 0 8px 32px 0 rgba(31, 41, 55, 0.18);
|
126
|
+
position: relative;
|
127
|
+
}
|
128
|
+
|
129
|
+
.orionis-modal-close {
|
130
|
+
position: absolute;
|
131
|
+
top: 0.7em;
|
132
|
+
right: 1em;
|
133
|
+
font-size: 1.3em;
|
134
|
+
color: #64748b;
|
135
|
+
background: none;
|
136
|
+
border: none;
|
137
|
+
cursor: pointer;
|
138
|
+
}
|
139
|
+
|
140
|
+
.orionis-modal-title {
|
141
|
+
font-size: 0.98em; /* reducido de 1.08em */
|
142
|
+
font-weight: 600;
|
143
|
+
margin-bottom: 0.7em;
|
144
|
+
color: #3730a3;
|
145
|
+
display: flex;
|
146
|
+
align-items: center;
|
147
|
+
gap: 0.5em;
|
148
|
+
font-family: inherit;
|
149
|
+
}
|
150
|
+
|
151
|
+
.orionis-modal-pre {
|
152
|
+
white-space: pre-wrap;
|
153
|
+
font-size: 0.92em;
|
154
|
+
color: #334155;
|
155
|
+
background: #f1f5f9;
|
156
|
+
padding: 1em;
|
157
|
+
border-radius: 0.7em;
|
158
|
+
max-height: 320px;
|
159
|
+
overflow: auto;
|
160
|
+
font-family: 'Fira Mono', 'Consolas', 'Menlo', 'Monaco', 'Liberation Mono', monospace;
|
161
|
+
}
|
162
|
+
</style>
|
163
|
+
</head>
|
164
|
+
<body class="bg-gray-100 text-gray-800 font-sans">
|
165
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 py-8">
|
166
|
+
|
167
|
+
<!-- Header -->
|
168
|
+
<header class="bg-gradient-to-r from-blue-900 to-cyan-400 text-white rounded-2xl shadow-xl p-6 mb-10">
|
169
|
+
<div class="flex flex-col md:flex-row justify-between items-center gap-4">
|
170
|
+
<div class="flex items-center gap-4">
|
171
|
+
<img src="https://orionis-framework.com/svg/logo.svg" alt="Orionis Logo"
|
172
|
+
class="h-10 brightness-0 invert" />
|
173
|
+
<h1 class="text-2xl font-light tracking-wider">Orionis Testing Results Dashboard</h1>
|
174
|
+
</div>
|
175
|
+
<div id="timestamp" class="text-sm text-white/90"></div>
|
176
|
+
</header>
|
177
|
+
|
178
|
+
<!-- Execution Summary Card -->
|
179
|
+
<div class="w-full mb-10">
|
180
|
+
<div class="bg-white rounded-2xl shadow-lg p-6 border-t-4 border-indigo-500 flex flex-col sm:flex-row items-center justify-between gap-4">
|
181
|
+
<div>
|
182
|
+
<div class="flex items-center gap-2 text-xs font-semibold text-gray-500 uppercase">
|
183
|
+
<i class="bi bi-graph-up-arrow text-indigo-500 text-base"></i>
|
184
|
+
<span>Execution Summary</span>
|
185
|
+
<span class="inline-flex items-center gap-1 bg-indigo-100 text-indigo-700 px-2 py-0.5 rounded-full text-[0.7em] font-bold ml-2">
|
186
|
+
<i class="bi bi-lightning-charge-fill text-yellow-500"></i> {{orionis-testing-persistent}}
|
187
|
+
</span>
|
188
|
+
</div>
|
189
|
+
<div class="text-lg font-bold text-gray-800 mt-2" id="execution-summary-title">Select an execution</div>
|
190
|
+
<div class="text-sm text-gray-600 mt-1" id="execution-summary-desc">Select an execution to view the summary.</div>
|
191
|
+
</div>
|
192
|
+
<div class="flex items-center gap-4">
|
193
|
+
<div class="flex items-center gap-1 text-gray-700">
|
194
|
+
<span id="execution-time">Duration: --:--:--</span>
|
195
|
+
</div>
|
196
|
+
</div>
|
197
|
+
</div>
|
198
|
+
</div>
|
199
|
+
|
200
|
+
<!-- Summary Cards -->
|
201
|
+
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6 mb-10">
|
202
|
+
|
203
|
+
<div class="bg-white rounded-2xl shadow-lg p-6 border-t-4 border-green-500">
|
204
|
+
<div class="text-xs font-semibold text-gray-500 uppercase">Passed</div>
|
205
|
+
<div class="text-4xl font-bold text-gray-800 mt-2" id="passed">0</div>
|
206
|
+
<div class="mt-4 bg-gray-200 rounded-full h-2">
|
207
|
+
<div class="bg-green-500 h-2 rounded-full" id="passed-progress" style="width: 0%"></div>
|
208
|
+
</div>
|
209
|
+
</div>
|
210
|
+
|
211
|
+
<div class="bg-white rounded-2xl shadow-lg p-6 border-t-4 border-red-500">
|
212
|
+
<div class="text-xs font-semibold text-gray-500 uppercase">Failed</div>
|
213
|
+
<div class="text-4xl font-bold text-gray-800 mt-2" id="failed">0</div>
|
214
|
+
</div>
|
215
|
+
|
216
|
+
<div class="bg-white rounded-2xl shadow-lg p-6 border-t-4 border-yellow-500">
|
217
|
+
<div class="text-xs font-semibold text-gray-500 uppercase">Errors</div>
|
218
|
+
<div class="text-4xl font-bold text-gray-800 mt-2" id="errors">0</div>
|
219
|
+
</div>
|
220
|
+
|
221
|
+
<div class="bg-white rounded-2xl shadow-lg p-6 border-t-4 border-blue-500">
|
222
|
+
<div class="text-xs font-semibold text-gray-500 uppercase">Skipped</div>
|
223
|
+
<div class="text-4xl font-bold text-gray-800 mt-2" id="skipped">0</div>
|
224
|
+
</div>
|
225
|
+
|
226
|
+
</div>
|
227
|
+
|
228
|
+
<!-- Download Buttons & Select -->
|
229
|
+
<div class="flex flex-wrap justify-between items-center mb-10 gap-4">
|
230
|
+
<!-- Buttons to the left -->
|
231
|
+
<div class="flex flex-wrap gap-4">
|
232
|
+
<button disabled id="download-json" class="flex items-center gap-2 bg-blue-300 text-white px-4 py-2 rounded shadow cursor-not-allowed opacity-60">
|
233
|
+
<i class="bi bi-file-earmark-code-fill text-lg"></i>
|
234
|
+
<span>Download JSON</span>
|
235
|
+
</button>
|
236
|
+
</div>
|
237
|
+
<!-- Elegant Select to the right -->
|
238
|
+
<div>
|
239
|
+
<select class="appearance-none bg-white border border-gray-300 text-gray-700 py-2 px-4 pr-10 rounded-lg shadow focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-blue-400 transition text-base font-medium" id="test-select">
|
240
|
+
</select>
|
241
|
+
</div>
|
242
|
+
</div>
|
243
|
+
|
244
|
+
<!-- Table Summary View with Grid.js -->
|
245
|
+
<div class="bg-white rounded-xl shadow-md p-5 border border-indigo-100">
|
246
|
+
<div class="flex items-center gap-2 mb-3">
|
247
|
+
<i class="bi bi-table text-lg text-indigo-500"></i>
|
248
|
+
<h2 class="text-lg font-semibold text-indigo-900">Execution Details</h2>
|
249
|
+
</div>
|
250
|
+
<div class="text-gray-500 mb-4 text-sm">
|
251
|
+
See all test cases for the selected execution.<br>Click
|
252
|
+
<span class="inline-flex items-center gap-1 px-1.5 py-0.5 bg-blue-50 text-blue-700 rounded text-xs font-mono">
|
253
|
+
<i class="bi bi-journal-text"></i> View
|
254
|
+
</span>
|
255
|
+
to show the docstring.
|
256
|
+
</div>
|
257
|
+
<div id="test-table"></div>
|
258
|
+
</div>
|
259
|
+
|
260
|
+
<!-- Footer -->
|
261
|
+
<footer class="mt-12 text-center text-gray-500 text-sm py-6">
|
262
|
+
Developed with the power of
|
263
|
+
<a href="https://orionis-framework.com/" target="_blank" rel="noopener"
|
264
|
+
class="font-semibold text-blue-700 hover:underline">
|
265
|
+
Orionis Framework
|
266
|
+
</a>
|
267
|
+
<i class="bi bi-stars text-yellow-400 align-middle ml-1"></i>
|
268
|
+
</footer>
|
269
|
+
|
270
|
+
<!-- Scripts -->
|
271
|
+
<script src="https://unpkg.com/gridjs/dist/gridjs.umd.js"></script>
|
272
|
+
<script>
|
273
|
+
|
274
|
+
// Result Orionis Testing
|
275
|
+
const data = {{orionis-testing-result}};
|
276
|
+
|
277
|
+
// Live Clock (Header Timestamp)
|
278
|
+
function updateClock() {
|
279
|
+
const now = new Date();
|
280
|
+
const formatted = now.toLocaleString('en-US', {
|
281
|
+
year: 'numeric',
|
282
|
+
month: '2-digit',
|
283
|
+
day: '2-digit',
|
284
|
+
hour: '2-digit',
|
285
|
+
minute: '2-digit',
|
286
|
+
second: '2-digit',
|
287
|
+
hour12: false
|
288
|
+
});
|
289
|
+
document.getElementById("timestamp").textContent = `Current time: ${formatted}`;
|
290
|
+
}
|
291
|
+
updateClock();
|
292
|
+
setInterval(updateClock, 1000);
|
293
|
+
|
294
|
+
// Populate Dropdown in English
|
295
|
+
let html = '<option selected disabled>Select an execution</option>';
|
296
|
+
data.forEach((item, i) => {
|
297
|
+
const date = new Date(item.timestamp);
|
298
|
+
const formattedDate = `${date.getFullYear()}-${(date.getMonth() + 1)
|
299
|
+
.toString()
|
300
|
+
.padStart(2, '0')}-${date.getDate()
|
301
|
+
.toString()
|
302
|
+
.padStart(2, '0')} ${date.getHours()
|
303
|
+
.toString()
|
304
|
+
.padStart(2, '0')}:${date.getMinutes()
|
305
|
+
.toString()
|
306
|
+
.padStart(2, '0')}:${date.getSeconds()
|
307
|
+
.toString()
|
308
|
+
.padStart(2, '0')}`;
|
309
|
+
html += `<option value="${i}">Execution ${i + 1} - ${formattedDate}</option>`;
|
310
|
+
});
|
311
|
+
document.getElementById("test-select").innerHTML = html;
|
312
|
+
|
313
|
+
// Event Listener for the dropdown
|
314
|
+
document.getElementById("test-select").addEventListener("change", function () {
|
315
|
+
const selectedIndex = this.value;
|
316
|
+
const selectedData = data[selectedIndex];
|
317
|
+
|
318
|
+
// Animate Counters
|
319
|
+
function animateValue(id, start, end, duration) {
|
320
|
+
const obj = document.getElementById(id);
|
321
|
+
let startTimestamp = null;
|
322
|
+
const step = (timestamp) => {
|
323
|
+
if (!startTimestamp) startTimestamp = timestamp;
|
324
|
+
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
|
325
|
+
obj.textContent = Math.floor(progress * (end - start) + start);
|
326
|
+
if (progress < 1) {
|
327
|
+
window.requestAnimationFrame(step);
|
328
|
+
} else {
|
329
|
+
obj.textContent = end;
|
330
|
+
}
|
331
|
+
};
|
332
|
+
window.requestAnimationFrame(step);
|
333
|
+
}
|
334
|
+
animateValue("passed", Number(document.getElementById("passed").textContent), selectedData.passed, 500);
|
335
|
+
animateValue("failed", Number(document.getElementById("failed").textContent), selectedData.failed, 500);
|
336
|
+
animateValue("errors", Number(document.getElementById("errors").textContent), selectedData.errors, 500);
|
337
|
+
animateValue("skipped", Number(document.getElementById("skipped").textContent), selectedData.skipped, 500);
|
338
|
+
|
339
|
+
// Animate Progress Bar
|
340
|
+
const passedBar = document.getElementById("passed-progress");
|
341
|
+
passedBar.style.transition = "width 0.6s cubic-bezier(0.4,0,0.2,1)";
|
342
|
+
const passedPercentage = (selectedData.passed / selectedData.total_tests) * 100;
|
343
|
+
passedBar.style.width = `${passedPercentage}%`;
|
344
|
+
|
345
|
+
// Update Execution Summary
|
346
|
+
const date = new Date(selectedData.timestamp);
|
347
|
+
const formattedDate = `${date.getFullYear()}-${(date.getMonth()+1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}:${date.getSeconds().toString().padStart(2, '0')}`;
|
348
|
+
document.getElementById("execution-summary-title").innerHTML = `<span class="inline-flex items-center gap-2"><i class="bi bi-activity text-indigo-500"></i> Execution - <span class="font-mono">${formattedDate}</span></span>`;
|
349
|
+
document.getElementById("execution-summary-desc").innerHTML =
|
350
|
+
`<span class="inline-flex gap-2 flex-wrap">
|
351
|
+
<span class="bg-green-100 text-green-800 px-2 py-1 rounded text-xs font-semibold"><i class="bi bi-check-circle-fill"></i> Passed: ${selectedData.passed}</span>
|
352
|
+
<span class="bg-red-100 text-red-800 px-2 py-1 rounded text-xs font-semibold"><i class="bi bi-x-circle-fill"></i> Failed: ${selectedData.failed}</span>
|
353
|
+
<span class="bg-yellow-100 text-yellow-800 px-2 py-1 rounded text-xs font-semibold"><i class="bi bi-exclamation-triangle-fill"></i> Errors: ${selectedData.errors}</span>
|
354
|
+
<span class="bg-blue-100 text-blue-800 px-2 py-1 rounded text-xs font-semibold"><i class="bi bi-skip-forward-fill"></i> Skipped: ${selectedData.skipped}</span>
|
355
|
+
<span class="bg-gray-100 text-gray-800 px-2 py-1 rounded text-xs font-semibold"><i class="bi bi-list-ol"></i> Total: ${selectedData.total_tests}</span>
|
356
|
+
<span class="bg-indigo-100 text-indigo-800 px-2 py-1 rounded text-xs font-semibold"><i class="bi bi-bar-chart-fill"></i> Success: ${selectedData.success_rate.toFixed(2)}%</span>
|
357
|
+
</span>`;
|
358
|
+
|
359
|
+
// Update Duration
|
360
|
+
function formatTime(seconds) {
|
361
|
+
const h = Math.floor(seconds / 3600).toString().padStart(2, '0');
|
362
|
+
const m = Math.floor((seconds % 3600) / 60).toString().padStart(2, '0');
|
363
|
+
const s = Math.floor(seconds % 60).toString().padStart(2, '0');
|
364
|
+
return `${h}:${m}:${s}`;
|
365
|
+
}
|
366
|
+
document.getElementById("execution-time").innerHTML = `<i class="bi bi-clock-history text-indigo-500"></i> Duration: <span class="font-mono">${formatTime(selectedData.total_time)}</span>`;
|
367
|
+
|
368
|
+
// Enable Download Button
|
369
|
+
document.getElementById("download-json").disabled = false;
|
370
|
+
document.getElementById("download-json").classList.remove("bg-blue-300", "cursor-not-allowed", "opacity-60");
|
371
|
+
document.getElementById("download-json").classList.add("bg-blue-600", "hover:bg-blue-700", "cursor-pointer", "opacity-100");
|
372
|
+
});
|
373
|
+
|
374
|
+
// Download JSON report with execution date and time in filename
|
375
|
+
document.getElementById("download-json").addEventListener("click", function () {
|
376
|
+
const btn = this;
|
377
|
+
const select = document.getElementById("test-select");
|
378
|
+
const selectedIndex = select.value;
|
379
|
+
if (selectedIndex === "" || selectedIndex === null || isNaN(selectedIndex)) return;
|
380
|
+
|
381
|
+
// Disable button and show loading state
|
382
|
+
btn.disabled = true;
|
383
|
+
btn.classList.add("opacity-60", "cursor-not-allowed");
|
384
|
+
btn.classList.remove("bg-blue-600", "hover:bg-blue-700", "cursor-pointer");
|
385
|
+
const originalText = btn.innerHTML;
|
386
|
+
btn.innerHTML = `<span class="animate-spin mr-2"><i class="bi bi-arrow-repeat"></i></span> Generating...`;
|
387
|
+
|
388
|
+
setTimeout(() => {
|
389
|
+
const selectedData = data[selectedIndex];
|
390
|
+
const jsonString = JSON.stringify(selectedData, null, 2);
|
391
|
+
const date = new Date(selectedData.timestamp);
|
392
|
+
// Format: YYYYMMDD_HHMMSS
|
393
|
+
const formattedDate = `${date.getFullYear()}${(date.getMonth()+1).toString().padStart(2,'0')}${date.getDate().toString().padStart(2,'0')}_${date.getHours().toString().padStart(2,'0')}${date.getMinutes().toString().padStart(2,'0')}${date.getSeconds().toString().padStart(2,'0')}`;
|
394
|
+
const filename = `test_report_${formattedDate}.json`;
|
395
|
+
const blob = new Blob([jsonString], { type: "application/json" });
|
396
|
+
const url = URL.createObjectURL(blob);
|
397
|
+
const a = document.createElement("a");
|
398
|
+
a.href = url;
|
399
|
+
a.download = filename;
|
400
|
+
document.body.appendChild(a);
|
401
|
+
a.click();
|
402
|
+
document.body.removeChild(a);
|
403
|
+
URL.revokeObjectURL(url);
|
404
|
+
|
405
|
+
// Restore button state
|
406
|
+
btn.disabled = false;
|
407
|
+
btn.classList.remove("opacity-60", "cursor-not-allowed");
|
408
|
+
btn.classList.add("bg-blue-600", "hover:bg-blue-700", "cursor-pointer");
|
409
|
+
btn.innerHTML = originalText;
|
410
|
+
}, 600);
|
411
|
+
});
|
412
|
+
|
413
|
+
// Show doc string in a modal dialog (event delegation)
|
414
|
+
function showDocString(doc) {
|
415
|
+
// Remove any existing modal
|
416
|
+
const oldModal = document.querySelector('.orionis-modal');
|
417
|
+
if (oldModal) oldModal.remove();
|
418
|
+
|
419
|
+
// Create modal element
|
420
|
+
const modal = document.createElement('div');
|
421
|
+
modal.className = 'orionis-modal';
|
422
|
+
modal.style.display = 'flex'; // Ensure modal is visible
|
423
|
+
|
424
|
+
modal.innerHTML = `
|
425
|
+
<div class="orionis-modal-content">
|
426
|
+
<button class="orionis-modal-close" aria-label="Cerrar" type="button">×</button>
|
427
|
+
<div class="orionis-modal-title">
|
428
|
+
<i class="bi bi-journal-text"></i> Docstring
|
429
|
+
</div>
|
430
|
+
<pre class="orionis-modal-pre">${doc ? String(doc).replace(/</g, "<").replace(/>/g, ">") : 'No docstring.'}</pre>
|
431
|
+
</div>
|
432
|
+
`;
|
433
|
+
// Close modal when clicking outside content
|
434
|
+
modal.addEventListener('mousedown', function (e) {
|
435
|
+
if (e.target === modal) modal.remove();
|
436
|
+
});
|
437
|
+
// Close modal when clicking close button
|
438
|
+
modal.querySelector('.orionis-modal-close').onclick = () => modal.remove();
|
439
|
+
document.body.appendChild(modal);
|
440
|
+
}
|
441
|
+
|
442
|
+
function renderTestTable(testDetails) {
|
443
|
+
if (window.testGrid) {
|
444
|
+
try { window.testGrid.destroy(); } catch (e) { }
|
445
|
+
}
|
446
|
+
const tableDiv = document.getElementById("test-table");
|
447
|
+
tableDiv.innerHTML = "";
|
448
|
+
|
449
|
+
try {
|
450
|
+
window.testGrid = new gridjs.Grid({
|
451
|
+
columns: [
|
452
|
+
{ name: "ID", width: "22%" },
|
453
|
+
{ name: "Class", width: "12%" },
|
454
|
+
{ name: "Method", width: "14%" },
|
455
|
+
{ name: "Status", width: "11%" },
|
456
|
+
{ name: "Time (s)", width: "9%" },
|
457
|
+
{ name: "File", width: "22%" },
|
458
|
+
{ name: "Doc", width: "10%" }
|
459
|
+
],
|
460
|
+
data: testDetails.map((t, idx) => [
|
461
|
+
gridjs.html(`<div title="${t.id}">${t.id}</div>`),
|
462
|
+
gridjs.html(`<div title="${t.class}">${t.class}</div>`),
|
463
|
+
gridjs.html(`<div title="${t.method}">${t.method}</div>`),
|
464
|
+
gridjs.html(`
|
465
|
+
<span class="badge-status ${
|
466
|
+
t.status === 'PASSED' ? 'badge-passed' :
|
467
|
+
t.status === 'FAILED' ? 'badge-failed' :
|
468
|
+
t.status === 'ERRORS' ? 'badge-errors' : 'badge-skipped'
|
469
|
+
} status-cell">
|
470
|
+
${
|
471
|
+
t.status === 'PASSED' ? '<i class="bi bi-check-circle-fill"></i>' :
|
472
|
+
t.status === 'FAILED' ? '<i class="bi bi-x-circle-fill"></i>' :
|
473
|
+
t.status === 'ERRORS' ? '<i class="bi bi-exclamation-triangle-fill"></i>' :
|
474
|
+
'<i class="bi bi-skip-forward-fill"></i>'
|
475
|
+
}
|
476
|
+
${t.status}
|
477
|
+
</span>
|
478
|
+
`),
|
479
|
+
t.execution_time,
|
480
|
+
gridjs.html(`<div title="${t.file_path}">${t.file_path}</div>`),
|
481
|
+
gridjs.html(`<button class="doc-btn doc-cell" data-doc-idx="${idx}" type="button"><i class="bi bi-journal-text"></i> Detail</button>`)
|
482
|
+
]),
|
483
|
+
search: {
|
484
|
+
enabled: true,
|
485
|
+
placeholder: 'Buscar solo por clase...'
|
486
|
+
},
|
487
|
+
pagination: { limit: 8, summary: false },
|
488
|
+
sort: false,
|
489
|
+
resizable: true,
|
490
|
+
style: {
|
491
|
+
th: { 'text-align': 'left', 'background': '#f1f5f9', 'font-size': '1em' }
|
492
|
+
}
|
493
|
+
}).render(tableDiv);
|
494
|
+
|
495
|
+
// Delegated event for doc buttons (always works, even after pagination)
|
496
|
+
tableDiv.addEventListener('click', function (e) {
|
497
|
+
const btn = e.target.closest('.doc-btn');
|
498
|
+
if (!btn) return;
|
499
|
+
const idx = Number(btn.getAttribute('data-doc-idx'));
|
500
|
+
const t = testDetails[idx];
|
501
|
+
if (!t) return;
|
502
|
+
|
503
|
+
let html = '';
|
504
|
+
html += `<div class="orionis-modal-title"><i class="bi bi-journal-text"></i> Docstring</div>`;
|
505
|
+
html += `<pre class="orionis-modal-pre">${t.doc_string ? String(t.doc_string).replace(/</g, "<").replace(/>/g, ">") : 'No docstring.'}</pre>`;
|
506
|
+
if (t.status === 'FAILED' || t.status === 'ERRORS') {
|
507
|
+
html += `<div class="orionis-modal-title mt-4"><i class="bi bi-bug-fill"></i> Traceback</div>`;
|
508
|
+
html += `<pre class="orionis-modal-pre" style="background:#fee2e2;color:#991b1b">${t.traceback ? String(t.traceback).replace(/</g, "<").replace(/>/g, ">") : 'No traceback.'}</pre>`;
|
509
|
+
}
|
510
|
+
|
511
|
+
// Remove any existing modal
|
512
|
+
const oldModal = document.querySelector('.orionis-modal');
|
513
|
+
if (oldModal) oldModal.remove();
|
514
|
+
|
515
|
+
// Create modal element
|
516
|
+
const modal = document.createElement('div');
|
517
|
+
modal.className = 'orionis-modal';
|
518
|
+
modal.style.display = 'flex';
|
519
|
+
|
520
|
+
modal.innerHTML = `
|
521
|
+
<div class="orionis-modal-content">
|
522
|
+
<button class="orionis-modal-close" aria-label="Cerrar" type="button">×</button>
|
523
|
+
${html}
|
524
|
+
</div>
|
525
|
+
`;
|
526
|
+
|
527
|
+
// Close modal when clicking outside content
|
528
|
+
modal.addEventListener('mousedown', function (e) {
|
529
|
+
if (e.target === modal) modal.remove();
|
530
|
+
});
|
531
|
+
|
532
|
+
// Close modal when clicking close button
|
533
|
+
modal.querySelector('.orionis-modal-close').onclick = () => modal.remove();
|
534
|
+
document.body.appendChild(modal);
|
535
|
+
});
|
536
|
+
} catch (err) {
|
537
|
+
tableDiv.innerHTML = `<div class="text-red-600 font-mono text-sm py-4">Ocurrió un error al mostrar la tabla.<br>${err.message}</div>`;
|
538
|
+
}
|
539
|
+
}
|
540
|
+
|
541
|
+
document.getElementById("test-select").addEventListener("change", function () {
|
542
|
+
const selectedIndex = this.value;
|
543
|
+
if (!data[selectedIndex]) return;
|
544
|
+
renderTestTable(data[selectedIndex].test_details);
|
545
|
+
});
|
546
|
+
|
547
|
+
document.addEventListener("DOMContentLoaded", function () {
|
548
|
+
renderTestTable([]);
|
549
|
+
});
|
550
|
+
|
551
|
+
// Show an elegant console message every second using setInterval
|
552
|
+
setInterval(function () {
|
553
|
+
console.clear();
|
554
|
+
console.log(
|
555
|
+
"%c✨ Developed with the power of %cOrionis Framework%c 🚀",
|
556
|
+
"color:#6366f1;font-weight:bold;font-size:1.1em;",
|
557
|
+
"color:#0ea5e9;font-weight:bold;font-size:1.15em;text-shadow:0 1px 0 #fff;",
|
558
|
+
"color:#f59e42;font-size:1.1em;"
|
559
|
+
);
|
560
|
+
}, 1000);
|
561
|
+
|
562
|
+
</script>
|
563
|
+
</body>
|
564
|
+
|
565
|
+
</html>
|
@@ -81,6 +81,8 @@ orionis/console/output/console.py,sha256=EtSDWRBW8wk0iJdPfB1mzU49krLJBaSAUdVdVOz
|
|
81
81
|
orionis/console/output/executor.py,sha256=uQjFPOlyZLFj9pcyYPugCqxwJog0AJgK1OcmQH2ELbw,7314
|
82
82
|
orionis/console/request/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
83
83
|
orionis/console/request/cli_request.py,sha256=zFuoFzLVDCcSNcJZAuLAlsc9jfhck5o23qaKc2C0QAg,13231
|
84
|
+
orionis/console/stub/command.stub,sha256=kRBdSqt1eBPe3W8xDR5meQBXl3JuRcs75Q6kmfkyRKM,733
|
85
|
+
orionis/console/stub/listener.stub,sha256=aFod_RZhoqo1W7l0S7fFBJ0-S46bQqwRAOr47ru8XYU,4846
|
84
86
|
orionis/console/tasks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
85
87
|
orionis/console/tasks/schedule.py,sha256=qFnzoLyK69iaKEWMfEVxLs3S9aWVRzD4G6emTFhJMaY,83386
|
86
88
|
orionis/container/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -241,7 +243,7 @@ orionis/foundation/providers/scheduler_provider.py,sha256=1do4B09bU_6xbFHHVYYTGM
|
|
241
243
|
orionis/foundation/providers/testing_provider.py,sha256=SrJRpdvcblx9WvX7x9Y3zc7OQfiTf7la0HAJrm2ESlE,3725
|
242
244
|
orionis/foundation/providers/workers_provider.py,sha256=oa_2NIDH6UxZrtuGkkoo_zEoNIMGgJ46vg5CCgAm7wI,3926
|
243
245
|
orionis/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
244
|
-
orionis/metadata/framework.py,sha256=
|
246
|
+
orionis/metadata/framework.py,sha256=qFRLVT7ha-_n0m8JiCXRHsbpTM9LmzALdqg8BhOPS_A,4109
|
245
247
|
orionis/metadata/package.py,sha256=k7Yriyp5aUcR-iR8SK2ec_lf0_Cyc-C7JczgXa-I67w,16039
|
246
248
|
orionis/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
247
249
|
orionis/services/asynchrony/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -433,8 +435,9 @@ orionis/test/validators/web_report.py,sha256=n9BfzOZz6aEiNTypXcwuWbFRG0OdHNSmCNu
|
|
433
435
|
orionis/test/validators/workers.py,sha256=rWcdRexINNEmGaO7mnc1MKUxkHKxrTsVuHgbnIfJYgc,1206
|
434
436
|
orionis/test/view/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
435
437
|
orionis/test/view/render.py,sha256=f-zNhtKSg9R5Njqujbg2l2amAs2-mRVESneLIkWOZjU,4082
|
436
|
-
orionis
|
437
|
-
orionis-0.
|
438
|
-
orionis-0.
|
439
|
-
orionis-0.
|
440
|
-
orionis-0.
|
438
|
+
orionis/test/view/report.stub,sha256=QLqqCdRoENr3ECiritRB3DO_MOjRQvgBh5jxZ3Hs1r0,28189
|
439
|
+
orionis-0.562.0.dist-info/licenses/LICENCE,sha256=JhC-z_9mbpUrCfPjcl3DhDA8trNDMzb57cvRSam1avc,1463
|
440
|
+
orionis-0.562.0.dist-info/METADATA,sha256=3Zyfs3T84Cpzo8KD2YB84MT4imnwHt0QHi8N8Ypb3JI,4801
|
441
|
+
orionis-0.562.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
442
|
+
orionis-0.562.0.dist-info/top_level.txt,sha256=lyXi6jArpqJ-0zzNqd_uwsH-z9TCEBVBL-pC3Ekv7hU,8
|
443
|
+
orionis-0.562.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|