skopix 2.0.1 → 2.0.2
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.
- package/cli/commands/agent.js +19 -0
- package/cli/index.js +3 -3
- package/package.json +1 -1
package/cli/commands/agent.js
CHANGED
|
@@ -274,6 +274,25 @@ export async function agentCommand(options) {
|
|
|
274
274
|
duration: 0, type: 'replay', provider: 'replay',
|
|
275
275
|
}, { spaces: 2 }).catch(() => {});
|
|
276
276
|
|
|
277
|
+
// Write report.html — two-column layout: video on left, step list on right
|
|
278
|
+
try {
|
|
279
|
+
const duration = '0.0';
|
|
280
|
+
const stepRows = allSteps.slice(0, stepNum).map((s, i) => {
|
|
281
|
+
const desc = (s.description || (s.action + ' ' + (s.stableSelector || s.selector || ''))).replace(/</g, '<').replace(/>/g, '>');
|
|
282
|
+
const screenshotFile = 'step-' + String(i + 1).padStart(3, '0') + '.png';
|
|
283
|
+
const isAssert = s.action === 'assert';
|
|
284
|
+
const isFailed = !passed && i === stepNum - 1;
|
|
285
|
+
if (isAssert) {
|
|
286
|
+
return '<div class="step assert-step" onclick="showStep(' + i + ')"><div class="step-num">' + String(i + 1).padStart(2, '0') + '</div><div class="step-body"><div class="step-action assert-action">✓ ' + (s.assertType || 'assert') + '</div><div class="step-desc">' + desc + '</div></div></div>';
|
|
287
|
+
}
|
|
288
|
+
return '<div class="step ' + (isFailed ? 'failed-step' : '') + '" onclick="showStep(' + i + ')"><div class="step-num">' + String(i + 1).padStart(2, '0') + '</div><div class="step-body"><div class="step-action">' + s.action.toUpperCase() + '</div><div class="step-desc">' + desc + '</div></div></div>';
|
|
289
|
+
}).join('\n');
|
|
290
|
+
|
|
291
|
+
const html = '<!DOCTYPE html><html><head><meta charset="utf-8"><title>Skopix Report — ' + test.name + '</title><style>body{margin:0;font-family:-apple-system,sans-serif;background:#0d0d1a;color:#e8eaf0}.container{display:grid;grid-template-columns:1fr 1fr;height:100vh}.left{padding:24px;background:#080810;border-right:1px solid rgba(255,255,255,0.08);display:flex;flex-direction:column}.right{padding:24px;overflow-y:auto}h1{margin:0 0 8px 0;font-size:24px}.meta{color:#5a6180;font-size:13px;margin-bottom:24px}.status{display:inline-block;padding:4px 12px;border-radius:4px;font-size:12px;font-weight:600;margin-left:8px}.status.passed{background:rgba(34,197,94,0.15);color:#22c55e}.status.failed{background:rgba(239,68,68,0.15);color:#ef4444}video,img{width:100%;border-radius:8px;background:#000}.step{padding:14px 16px;border-bottom:1px solid rgba(255,255,255,0.06);display:flex;gap:14px;cursor:pointer;transition:background 0.15s}.step:hover{background:rgba(0,212,255,0.06)}.step.active{background:rgba(0,212,255,0.1)}.step-num{font-family:monospace;color:#5a6180;font-size:12px;min-width:24px}.step-action{font-family:monospace;font-size:11px;color:#00d4ff;letter-spacing:0.05em;margin-bottom:4px}.assert-action{color:#22c55e}.step-desc{font-size:13px}.failed-step .step-action{color:#ef4444}</style></head><body><div class="container"><div class="left"><h1>' + test.name + '<span class="status ' + (passed ? 'passed' : 'failed') + '">' + (passed ? 'PASSED' : 'FAILED') + '</span></h1><div class="meta">' + allSteps.length + ' steps · ' + duration + 's · agent: ' + os.hostname() + '</div><div id="media"><video src="replay.webm" controls autoplay muted loop></video></div></div><div class="right">' + stepRows + '</div></div><script>function showStep(i){document.querySelectorAll(".step").forEach(s=>s.classList.remove("active"));document.querySelectorAll(".step")[i].classList.add("active");const img=document.createElement("img");img.src="step-"+String(i+1).padStart(3,"0")+".png";document.getElementById("media").innerHTML="";document.getElementById("media").appendChild(img);}</script></body></html>';
|
|
292
|
+
|
|
293
|
+
await fs.writeFile(path.join(sessionDir, 'report.html'), html);
|
|
294
|
+
} catch (e) { /* ignore report html errors */ }
|
|
295
|
+
|
|
277
296
|
send({ type: 'stdout', text: '' });
|
|
278
297
|
send({ type: 'stdout', text: '━'.repeat(60) });
|
|
279
298
|
send({ type: 'stdout', text: ' Status: ' + (passed ? 'PASSED ✓' : 'FAILED ✗') });
|
package/cli/index.js
CHANGED
|
@@ -32,7 +32,7 @@ program
|
|
|
32
32
|
.requiredOption('-u, --url <url>', 'Target URL to test')
|
|
33
33
|
.requiredOption('-g, --goal <goal>', 'Testing goal (e.g. "complete the checkout flow")')
|
|
34
34
|
.option('-c, --credentials <file>', 'Path to credentials YAML file')
|
|
35
|
-
.option('-o, --output <dir>', 'Output directory for reports'
|
|
35
|
+
.option('-o, --output <dir>', 'Output directory for reports')
|
|
36
36
|
.option('-m, --max-steps <number>', 'Maximum steps the agent will take', '20')
|
|
37
37
|
.option('--headless', 'Run browser in headless mode', false)
|
|
38
38
|
.option('--no-video', 'Disable video recording')
|
|
@@ -53,7 +53,7 @@ program
|
|
|
53
53
|
program
|
|
54
54
|
.command('report')
|
|
55
55
|
.description('Open the latest report in your browser')
|
|
56
|
-
.option('-d, --dir <dir>', 'Reports directory'
|
|
56
|
+
.option('-d, --dir <dir>', 'Reports directory')
|
|
57
57
|
.action(reportCommand);
|
|
58
58
|
|
|
59
59
|
program
|
|
@@ -68,7 +68,7 @@ program
|
|
|
68
68
|
.command('dashboard')
|
|
69
69
|
.description('Launch the web dashboard')
|
|
70
70
|
.option('-p, --port <port>', 'Port to run the server on', '9000')
|
|
71
|
-
.option('-d, --dir <dir>', 'Reports directory'
|
|
71
|
+
.option('-d, --dir <dir>', 'Reports directory')
|
|
72
72
|
.option('-h, --host <host>', 'Host to bind to (default 127.0.0.1; use 0.0.0.0 for team mode)')
|
|
73
73
|
.option('--team', 'Enable multi-user team mode (requires SQLite)')
|
|
74
74
|
.option('--no-open', 'Do not auto-open the browser')
|
package/package.json
CHANGED