nextog-cli 1.0.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.
- nextog/__init__.py +4 -0
- nextog/cli.py +545 -0
- nextog/config/__init__.py +1 -0
- nextog/config/settings.py +132 -0
- nextog/core/__init__.py +1 -0
- nextog/core/engine.py +193 -0
- nextog/core/permissions.py +129 -0
- nextog/core/privacy.py +130 -0
- nextog/core/reporter.py +204 -0
- nextog/core/runner.py +236 -0
- nextog/data/__init__.py +1 -0
- nextog/data/local_db.py +367 -0
- nextog/data/models.py +72 -0
- nextog/data/sync.py +65 -0
- nextog/engines/__init__.py +1 -0
- nextog/engines/api/__init__.py +1 -0
- nextog/engines/api/graphql.py +54 -0
- nextog/engines/api/rest.py +346 -0
- nextog/engines/api/websocket.py +59 -0
- nextog/engines/embedded/__init__.py +1 -0
- nextog/engines/embedded/firmware.py +53 -0
- nextog/engines/embedded/hardware.py +330 -0
- nextog/engines/mobile/__init__.py +1 -0
- nextog/engines/mobile/android.py +333 -0
- nextog/engines/mobile/cross.py +48 -0
- nextog/engines/mobile/ios.py +46 -0
- nextog/engines/system/__init__.py +1 -0
- nextog/engines/system/load.py +121 -0
- nextog/engines/system/performance.py +128 -0
- nextog/engines/system/security.py +170 -0
- nextog/engines/web/__init__.py +1 -0
- nextog/engines/web/accessibility.py +191 -0
- nextog/engines/web/browser.py +387 -0
- nextog/engines/web/elements.py +285 -0
- nextog/engines/web/responsive.py +79 -0
- nextog/live/__init__.py +1 -0
- nextog/live/dashboard.py +30 -0
- nextog/live/panel.py +325 -0
- nextog/reports/__init__.py +1359 -0
- nextog/training/__init__.py +1 -0
- nextog/training/learner.py +269 -0
- nextog/training/patterns.py +102 -0
- nextog/utils/__init__.py +1 -0
- nextog/utils/helpers.py +91 -0
- nextog/utils/logger.py +37 -0
- nextog/utils/validators.py +98 -0
- nextog_cli-1.0.0.dist-info/METADATA +344 -0
- nextog_cli-1.0.0.dist-info/RECORD +51 -0
- nextog_cli-1.0.0.dist-info/WHEEL +5 -0
- nextog_cli-1.0.0.dist-info/entry_points.txt +2 -0
- nextog_cli-1.0.0.dist-info/top_level.txt +1 -0
nextog/live/panel.py
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Live Testing Panel - Real-time testing dashboard
|
|
3
|
+
Uses FastAPI + WebSocket for live updates
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import asyncio
|
|
7
|
+
import json
|
|
8
|
+
import webbrowser
|
|
9
|
+
import threading
|
|
10
|
+
from typing import Dict, Any, Optional
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
|
|
15
|
+
console = Console()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class LivePanel:
|
|
19
|
+
"""Real-time testing dashboard with WebSocket updates"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, settings, db, privacy):
|
|
22
|
+
self.settings = settings
|
|
23
|
+
self.db = db
|
|
24
|
+
self.privacy = privacy
|
|
25
|
+
self.connected_clients = []
|
|
26
|
+
self.running = False
|
|
27
|
+
|
|
28
|
+
def start(self, host: str = "127.0.0.1", port: int = 8080, open_browser: bool = True):
|
|
29
|
+
"""Start the live dashboard"""
|
|
30
|
+
console.print(f"[bold green]🚀 Starting Live Dashboard on http://{host}:{port}[/bold green]")
|
|
31
|
+
console.print("[dim]Press Ctrl+C to stop[/dim]")
|
|
32
|
+
|
|
33
|
+
if open_browser:
|
|
34
|
+
threading.Timer(1.5, lambda: webbrowser.open(f"http://{host}:{port}")).start()
|
|
35
|
+
|
|
36
|
+
self._start_server(host, port)
|
|
37
|
+
|
|
38
|
+
def _start_server(self, host: str, port: int):
|
|
39
|
+
"""Start FastAPI server with WebSocket support"""
|
|
40
|
+
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
|
|
41
|
+
from fastapi.responses import HTMLResponse
|
|
42
|
+
import uvicorn
|
|
43
|
+
|
|
44
|
+
app = FastAPI(title="nextOG Live Dashboard")
|
|
45
|
+
|
|
46
|
+
@app.get("/", response_class=HTMLResponse)
|
|
47
|
+
async def dashboard():
|
|
48
|
+
return self._get_dashboard_html()
|
|
49
|
+
|
|
50
|
+
@app.get("/api/status")
|
|
51
|
+
async def status():
|
|
52
|
+
return {
|
|
53
|
+
"status": "running",
|
|
54
|
+
"coverage": self._get_current_coverage(),
|
|
55
|
+
"timestamp": datetime.now().isoformat(),
|
|
56
|
+
"active_tests": 0,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@app.get("/api/coverage")
|
|
60
|
+
async def coverage():
|
|
61
|
+
return self._get_current_coverage()
|
|
62
|
+
|
|
63
|
+
@app.get("/api/results")
|
|
64
|
+
async def results():
|
|
65
|
+
return self.db.get_latest_results()
|
|
66
|
+
|
|
67
|
+
@app.websocket("/ws")
|
|
68
|
+
async def websocket_endpoint(websocket: WebSocket):
|
|
69
|
+
await websocket.accept()
|
|
70
|
+
self.connected_clients.append(websocket)
|
|
71
|
+
try:
|
|
72
|
+
while True:
|
|
73
|
+
data = await websocket.receive_text()
|
|
74
|
+
# Handle client messages
|
|
75
|
+
if data == "ping":
|
|
76
|
+
await websocket.send_json({"type": "pong"})
|
|
77
|
+
elif data == "status":
|
|
78
|
+
await websocket.send_json(self._get_current_coverage())
|
|
79
|
+
except WebSocketDisconnect:
|
|
80
|
+
self.connected_clients.remove(websocket)
|
|
81
|
+
|
|
82
|
+
# Run server
|
|
83
|
+
uvicorn.run(app, host=host, port=port, log_level="warning")
|
|
84
|
+
|
|
85
|
+
def _get_current_coverage(self) -> Dict:
|
|
86
|
+
"""Get current coverage data"""
|
|
87
|
+
from nextog.core.reporter import CoverageReporter
|
|
88
|
+
reporter = CoverageReporter(self.db, self.settings)
|
|
89
|
+
return reporter.get_coverage()
|
|
90
|
+
|
|
91
|
+
def _get_dashboard_html(self) -> str:
|
|
92
|
+
"""Generate dashboard HTML"""
|
|
93
|
+
return """<!DOCTYPE html>
|
|
94
|
+
<html lang="en">
|
|
95
|
+
<head>
|
|
96
|
+
<meta charset="UTF-8">
|
|
97
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
98
|
+
<title>nextOG Live Dashboard</title>
|
|
99
|
+
<style>
|
|
100
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
101
|
+
body {
|
|
102
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
103
|
+
background: #0f172a;
|
|
104
|
+
color: #e2e8f0;
|
|
105
|
+
min-height: 100vh;
|
|
106
|
+
}
|
|
107
|
+
.header {
|
|
108
|
+
background: linear-gradient(135deg, #1e293b, #0f172a);
|
|
109
|
+
padding: 20px 40px;
|
|
110
|
+
border-bottom: 1px solid #334155;
|
|
111
|
+
display: flex;
|
|
112
|
+
justify-content: space-between;
|
|
113
|
+
align-items: center;
|
|
114
|
+
}
|
|
115
|
+
.header h1 { font-size: 1.5rem; color: #38bdf8; }
|
|
116
|
+
.status-badge {
|
|
117
|
+
padding: 4px 12px;
|
|
118
|
+
border-radius: 20px;
|
|
119
|
+
font-size: 0.8rem;
|
|
120
|
+
font-weight: bold;
|
|
121
|
+
}
|
|
122
|
+
.status-running { background: #065f46; color: #6ee7b7; }
|
|
123
|
+
.container { padding: 30px 40px; max-width: 1400px; margin: 0 auto; }
|
|
124
|
+
.stats-grid {
|
|
125
|
+
display: grid;
|
|
126
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
127
|
+
gap: 20px;
|
|
128
|
+
margin-bottom: 30px;
|
|
129
|
+
}
|
|
130
|
+
.stat-card {
|
|
131
|
+
background: #1e293b;
|
|
132
|
+
border-radius: 12px;
|
|
133
|
+
padding: 24px;
|
|
134
|
+
border: 1px solid #334155;
|
|
135
|
+
}
|
|
136
|
+
.stat-card h3 { font-size: 0.85rem; color: #94a3b8; margin-bottom: 8px; }
|
|
137
|
+
.stat-card .value { font-size: 2rem; font-weight: bold; }
|
|
138
|
+
.stat-card .value.green { color: #22c55e; }
|
|
139
|
+
.stat-card .value.blue { color: #38bdf8; }
|
|
140
|
+
.stat-card .value.yellow { color: #eab308; }
|
|
141
|
+
.coverage-section {
|
|
142
|
+
background: #1e293b;
|
|
143
|
+
border-radius: 12px;
|
|
144
|
+
padding: 30px;
|
|
145
|
+
border: 1px solid #334155;
|
|
146
|
+
margin-bottom: 20px;
|
|
147
|
+
}
|
|
148
|
+
.coverage-bar {
|
|
149
|
+
background: #334155;
|
|
150
|
+
border-radius: 8px;
|
|
151
|
+
height: 24px;
|
|
152
|
+
overflow: hidden;
|
|
153
|
+
margin: 15px 0;
|
|
154
|
+
}
|
|
155
|
+
.coverage-fill {
|
|
156
|
+
height: 100%;
|
|
157
|
+
border-radius: 8px;
|
|
158
|
+
transition: width 1s ease;
|
|
159
|
+
background: linear-gradient(90deg, #22c55e, #38bdf8);
|
|
160
|
+
}
|
|
161
|
+
.phases { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-top: 20px; }
|
|
162
|
+
.phase {
|
|
163
|
+
background: #0f172a;
|
|
164
|
+
border-radius: 8px;
|
|
165
|
+
padding: 16px;
|
|
166
|
+
border: 1px solid #334155;
|
|
167
|
+
}
|
|
168
|
+
.phase-header { display: flex; justify-content: space-between; margin-bottom: 8px; }
|
|
169
|
+
.phase-bar { background: #334155; border-radius: 4px; height: 6px; overflow: hidden; }
|
|
170
|
+
.phase-fill { height: 100%; border-radius: 4px; background: #38bdf8; transition: width 0.5s; }
|
|
171
|
+
.log-section {
|
|
172
|
+
background: #1e293b;
|
|
173
|
+
border-radius: 12px;
|
|
174
|
+
padding: 20px;
|
|
175
|
+
border: 1px solid #334155;
|
|
176
|
+
max-height: 300px;
|
|
177
|
+
overflow-y: auto;
|
|
178
|
+
}
|
|
179
|
+
.log-entry {
|
|
180
|
+
padding: 8px 0;
|
|
181
|
+
border-bottom: 1px solid #334155;
|
|
182
|
+
font-family: 'Fira Code', monospace;
|
|
183
|
+
font-size: 0.85rem;
|
|
184
|
+
}
|
|
185
|
+
.log-time { color: #64748b; }
|
|
186
|
+
.log-pass { color: #22c55e; }
|
|
187
|
+
.log-fail { color: #ef4444; }
|
|
188
|
+
.log-info { color: #38bdf8; }
|
|
189
|
+
.ws-status { font-size: 0.8rem; color: #64748b; }
|
|
190
|
+
.ws-connected { color: #22c55e; }
|
|
191
|
+
</style>
|
|
192
|
+
</head>
|
|
193
|
+
<body>
|
|
194
|
+
<div class="header">
|
|
195
|
+
<h1>🚀 nextOG Live Dashboard</h1>
|
|
196
|
+
<div>
|
|
197
|
+
<span class="status-badge status-running">● LIVE</span>
|
|
198
|
+
<span class="ws-status" id="ws-status">Connecting...</span>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<div class="container">
|
|
203
|
+
<div class="stats-grid">
|
|
204
|
+
<div class="stat-card">
|
|
205
|
+
<h3>Total Coverage</h3>
|
|
206
|
+
<div class="value green" id="total-coverage">0%</div>
|
|
207
|
+
</div>
|
|
208
|
+
<div class="stat-card">
|
|
209
|
+
<h3>Tests Passed</h3>
|
|
210
|
+
<div class="value blue" id="tests-passed">0</div>
|
|
211
|
+
</div>
|
|
212
|
+
<div class="stat-card">
|
|
213
|
+
<h3>Tests Failed</h3>
|
|
214
|
+
<div class="value yellow" id="tests-failed">0</div>
|
|
215
|
+
</div>
|
|
216
|
+
<div class="stat-card">
|
|
217
|
+
<h3>Active Sessions</h3>
|
|
218
|
+
<div class="value blue" id="active-sessions">1</div>
|
|
219
|
+
</div>
|
|
220
|
+
</div>
|
|
221
|
+
|
|
222
|
+
<div class="coverage-section">
|
|
223
|
+
<h2>📊 Coverage Progress (0% → 90%)</h2>
|
|
224
|
+
<div class="coverage-bar">
|
|
225
|
+
<div class="coverage-fill" id="coverage-fill" style="width: 0%"></div>
|
|
226
|
+
</div>
|
|
227
|
+
|
|
228
|
+
<div class="phases">
|
|
229
|
+
<div class="phase">
|
|
230
|
+
<div class="phase-header">
|
|
231
|
+
<span>🔥 Smoke Tests</span>
|
|
232
|
+
<span id="phase1-pct">0%</span>
|
|
233
|
+
</div>
|
|
234
|
+
<div class="phase-bar"><div class="phase-fill" id="phase1-bar" style="width: 0%"></div></div>
|
|
235
|
+
</div>
|
|
236
|
+
<div class="phase">
|
|
237
|
+
<div class="phase-header">
|
|
238
|
+
<span>⚡ Functional Tests</span>
|
|
239
|
+
<span id="phase2-pct">0%</span>
|
|
240
|
+
</div>
|
|
241
|
+
<div class="phase-bar"><div class="phase-fill" id="phase2-bar" style="width: 0%"></div></div>
|
|
242
|
+
</div>
|
|
243
|
+
<div class="phase">
|
|
244
|
+
<div class="phase-header">
|
|
245
|
+
<span>🔗 Integration Tests</span>
|
|
246
|
+
<span id="phase3-pct">0%</span>
|
|
247
|
+
</div>
|
|
248
|
+
<div class="phase-bar"><div class="phase-fill" id="phase3-bar" style="width: 0%"></div></div>
|
|
249
|
+
</div>
|
|
250
|
+
<div class="phase">
|
|
251
|
+
<div class="phase-header">
|
|
252
|
+
<span>🛡️ Performance & Security</span>
|
|
253
|
+
<span id="phase4-pct">0%</span>
|
|
254
|
+
</div>
|
|
255
|
+
<div class="phase-bar"><div class="phase-fill" id="phase4-bar" style="width: 0%"></div></div>
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
|
|
260
|
+
<div class="log-section">
|
|
261
|
+
<h3>📋 Live Test Log</h3>
|
|
262
|
+
<div id="log-entries">
|
|
263
|
+
<div class="log-entry"><span class="log-info">[INFO]</span> Dashboard connected. Waiting for test results...</div>
|
|
264
|
+
</div>
|
|
265
|
+
</div>
|
|
266
|
+
</div>
|
|
267
|
+
|
|
268
|
+
<script>
|
|
269
|
+
// WebSocket connection
|
|
270
|
+
const ws = new WebSocket(`ws://${window.location.host}/ws`);
|
|
271
|
+
|
|
272
|
+
ws.onopen = () => {
|
|
273
|
+
document.getElementById('ws-status').textContent = '● Connected';
|
|
274
|
+
document.getElementById('ws-status').className = 'ws-status ws-connected';
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
ws.onmessage = (event) => {
|
|
278
|
+
const data = JSON.parse(event.data);
|
|
279
|
+
updateDashboard(data);
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
ws.onclose = () => {
|
|
283
|
+
document.getElementById('ws-status').textContent = '● Disconnected';
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
// Poll for updates
|
|
287
|
+
async function fetchStatus() {
|
|
288
|
+
try {
|
|
289
|
+
const resp = await fetch('/api/status');
|
|
290
|
+
const data = await resp.json();
|
|
291
|
+
updateDashboard(data);
|
|
292
|
+
} catch(e) {}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function updateDashboard(data) {
|
|
296
|
+
const coverage = data.coverage?.total_coverage || data.total_coverage || 0;
|
|
297
|
+
|
|
298
|
+
document.getElementById('total-coverage').textContent = coverage + '%';
|
|
299
|
+
document.getElementById('coverage-fill').style.width = coverage + '%';
|
|
300
|
+
|
|
301
|
+
// Update phases if available
|
|
302
|
+
const phases = data.coverage?.phases || {};
|
|
303
|
+
let i = 1;
|
|
304
|
+
for (const [name, phaseData] of Object.entries(phases)) {
|
|
305
|
+
const pct = phaseData.coverage || 0;
|
|
306
|
+
document.getElementById(`phase${i}-pct`).textContent = pct + '%';
|
|
307
|
+
document.getElementById(`phase${i}-bar`).style.width = pct + '%';
|
|
308
|
+
i++;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Auto-refresh every 3 seconds
|
|
313
|
+
setInterval(fetchStatus, 3000);
|
|
314
|
+
fetchStatus();
|
|
315
|
+
</script>
|
|
316
|
+
</body>
|
|
317
|
+
</html>"""
|
|
318
|
+
|
|
319
|
+
async def broadcast(self, message: Dict):
|
|
320
|
+
"""Send message to all connected clients"""
|
|
321
|
+
for client in self.connected_clients:
|
|
322
|
+
try:
|
|
323
|
+
await client.send_json(message)
|
|
324
|
+
except Exception:
|
|
325
|
+
pass
|