testdriverai 7.5.0 → 7.5.9
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/{ai/skills/testdriver:quickstart → .github/skills/testdriver-quickstart}/SKILL.md +0 -1
- package/.github/workflows/acceptance.yaml +2 -1
- package/.github/workflows/publish.yaml +1 -1
- package/.github/workflows/windows-self-hosted.yaml +3 -2
- package/CHANGELOG.md +336 -0
- package/agent/index.js +22 -15
- package/agent/lib/commands.js +1 -1
- package/agent/lib/sandbox.js +115 -11
- package/agent/lib/sdk.js +2 -1
- package/agent/lib/system.js +48 -28
- package/{.github/skills/testdriver:quickstart → ai/skills/testdriver-quickstart}/SKILL.md +0 -2
- package/debugger/index.html +49 -368
- package/docs/_scripts/generate-skills.js +7 -2
- package/docs/v7/quickstart.mdx +0 -2
- package/interfaces/cli/lib/base.js +1 -1
- package/interfaces/vitest-plugin.mjs +6 -5
- package/lib/core/Dashcam.js +51 -24
- package/lib/sentry.js +1 -1
- package/package.json +1 -1
- package/sdk.js +21 -28
- package/vitest.config.mjs +5 -5
- package/vscode-extension/src/extension.ts +1 -1
- package/SKILLs.md +0 -17
- package/agent/lib/debugger-server.js +0 -236
- package/agent/lib/debugger.js +0 -45
- package/test-ide-preview.mjs +0 -17
- package/tests/airbnb-booking.test.mjs +0 -39
- package/tests/airbnb-search.test.mjs +0 -43
- package/tests/example.test.js +0 -33
- package/tests/login.js +0 -28
- /package/.github/skills/{testdriver:ai → testdriver-ai}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:assert → testdriver-assert}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:aws-setup → testdriver-aws-setup}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:caching → testdriver-caching}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:captcha → testdriver-captcha}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:ci-cd → testdriver-ci-cd}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:click → testdriver-click}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:client → testdriver-client}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:cloud → testdriver-cloud}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:customizing-devices → testdriver-customizing-devices}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:dashcam → testdriver-dashcam}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:debugging-with-screenshots → testdriver-debugging-with-screenshots}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:device-config → testdriver-device-config}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:double-click → testdriver-double-click}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:elements → testdriver-elements}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:enterprise → testdriver-enterprise}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:examples → testdriver-examples}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:exec → testdriver-exec}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:find → testdriver-find}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:focus-application → testdriver-focus-application}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:generating-tests → testdriver-generating-tests}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:hover → testdriver-hover}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:locating-elements → testdriver-locating-elements}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:making-assertions → testdriver-making-assertions}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:mcp-workflow → testdriver-mcp-workflow}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:mouse-down → testdriver-mouse-down}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:mouse-up → testdriver-mouse-up}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:parse → testdriver-parse}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:performing-actions → testdriver-performing-actions}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:press-keys → testdriver-press-keys}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:reusable-code → testdriver-reusable-code}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:right-click → testdriver-right-click}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:running-tests → testdriver-running-tests}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:screenshot → testdriver-screenshot}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:scroll → testdriver-scroll}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:secrets → testdriver-secrets}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:self-hosted → testdriver-self-hosted}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:test-writer → testdriver-test-writer}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:testdriver → testdriver-testdriver}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:testdriver-mechanic → testdriver-testdriver-mechanic}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:type → testdriver-type}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:variables → testdriver-variables}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:waiting-for-elements → testdriver-waiting-for-elements}/SKILL.md +0 -0
- /package/.github/skills/{testdriver:what-is-testdriver → testdriver-what-is-testdriver}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:ai → testdriver-ai}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:assert → testdriver-assert}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:aws-setup → testdriver-aws-setup}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:caching → testdriver-caching}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:captcha → testdriver-captcha}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:ci-cd → testdriver-ci-cd}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:click → testdriver-click}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:client → testdriver-client}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:cloud → testdriver-cloud}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:customizing-devices → testdriver-customizing-devices}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:dashcam → testdriver-dashcam}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:debugging-with-screenshots → testdriver-debugging-with-screenshots}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:device-config → testdriver-device-config}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:double-click → testdriver-double-click}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:elements → testdriver-elements}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:enterprise → testdriver-enterprise}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:examples → testdriver-examples}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:exec → testdriver-exec}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:find → testdriver-find}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:focus-application → testdriver-focus-application}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:generating-tests → testdriver-generating-tests}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:hover → testdriver-hover}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:locating-elements → testdriver-locating-elements}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:making-assertions → testdriver-making-assertions}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:mcp-workflow → testdriver-mcp-workflow}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:mouse-down → testdriver-mouse-down}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:mouse-up → testdriver-mouse-up}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:parse → testdriver-parse}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:performing-actions → testdriver-performing-actions}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:press-keys → testdriver-press-keys}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:reusable-code → testdriver-reusable-code}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:right-click → testdriver-right-click}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:running-tests → testdriver-running-tests}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:screenshot → testdriver-screenshot}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:scroll → testdriver-scroll}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:secrets → testdriver-secrets}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:self-hosted → testdriver-self-hosted}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:test-writer → testdriver-test-writer}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:testdriver → testdriver-testdriver}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:testdriver-mechanic → testdriver-testdriver-mechanic}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:type → testdriver-type}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:variables → testdriver-variables}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:waiting-for-elements → testdriver-waiting-for-elements}/SKILL.md +0 -0
- /package/ai/skills/{testdriver:what-is-testdriver → testdriver-what-is-testdriver}/SKILL.md +0 -0
- /package/{test/api-resilience.test.mjs → lib/vitest/gate-server.mjs} +0 -0
package/debugger/index.html
CHANGED
|
@@ -330,9 +330,6 @@
|
|
|
330
330
|
|
|
331
331
|
<div class="overlay" id="overlay">
|
|
332
332
|
<div class="effects" id="effects">
|
|
333
|
-
<div class="mouse" id="mouse"></div>
|
|
334
|
-
<div class="screenshot" id="screenshot"></div>
|
|
335
|
-
<div class="status" id="status"></div>
|
|
336
333
|
<div class="interaction-overlay" id="interaction-overlay">
|
|
337
334
|
<svg class="lock-icon" viewBox="0 0 24 24">
|
|
338
335
|
<path
|
|
@@ -346,219 +343,29 @@
|
|
|
346
343
|
</div>
|
|
347
344
|
|
|
348
345
|
<script>
|
|
349
|
-
//
|
|
346
|
+
// Parse session data from URL
|
|
350
347
|
const urlParams = new URLSearchParams(window.location.search);
|
|
351
348
|
const data = urlParams.get("data");
|
|
352
349
|
let parsedData;
|
|
353
350
|
if (data) {
|
|
354
351
|
try {
|
|
355
352
|
parsedData = JSON.parse(atob(data));
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
} catch (error) {
|
|
359
|
-
console.error("Error parsing data from URL:", error);
|
|
353
|
+
} catch (e) {
|
|
354
|
+
console.error("Error parsing data:", e);
|
|
360
355
|
}
|
|
361
|
-
} else {
|
|
362
|
-
alert(
|
|
363
|
-
"Improperly formatted URL. Please ensure the data parameter is present.",
|
|
364
|
-
);
|
|
365
356
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
// Detect if OS is Linux
|
|
370
|
-
const isLinux = parsedData.os === "linux";
|
|
371
|
-
const topBarOffset = isLinux ? 14 : 0;
|
|
372
|
-
|
|
373
|
-
// set overlay width and height to match the given resolution
|
|
374
|
-
const overlayWidth = parsedData.resolution[0];
|
|
375
|
-
const overlayHeight = parsedData.resolution[1];
|
|
376
|
-
|
|
377
|
-
iframe.style.display = "block";
|
|
378
|
-
iframe.src = parsedData.url;
|
|
379
|
-
iframe.style.width = overlayWidth + "px";
|
|
380
|
-
// Increase iframe height by 14px for Linux to account for top bar
|
|
381
|
-
iframe.style.height = overlayHeight + topBarOffset + "px";
|
|
382
|
-
|
|
383
|
-
// Calculate scale factor to fit within window if needed
|
|
384
|
-
const windowWidth = window.innerWidth;
|
|
385
|
-
const windowHeight = window.innerHeight;
|
|
386
|
-
const padding = 20; // Add some padding to prevent cropping
|
|
387
|
-
const scaleX = (windowWidth - padding * 2) / overlayWidth;
|
|
388
|
-
const scaleY = (windowHeight - padding * 2) / overlayHeight;
|
|
389
|
-
const scale = Math.min(scaleX, scaleY, 1); // Don't scale up, only down
|
|
390
|
-
|
|
391
|
-
console.log("window:", {
|
|
392
|
-
width: windowWidth,
|
|
393
|
-
height: windowHeight,
|
|
394
|
-
});
|
|
395
|
-
console.log("overlay:", {
|
|
396
|
-
width: overlayWidth,
|
|
397
|
-
height: overlayHeight,
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
const overlay = document.getElementById("overlay");
|
|
401
|
-
overlay.style.width = overlayWidth + "px";
|
|
402
|
-
overlay.style.height = overlayHeight + "px";
|
|
403
|
-
|
|
404
|
-
console.log("scale", scale);
|
|
405
|
-
// Apply scaling if needed
|
|
406
|
-
if (scale < 1) {
|
|
407
|
-
overlay.style.transform = `scale(${scale})`;
|
|
357
|
+
if (!parsedData || !parsedData.url) {
|
|
358
|
+
alert("Missing or invalid data parameter.");
|
|
408
359
|
}
|
|
409
360
|
|
|
410
|
-
//
|
|
411
|
-
const
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
// WebSocket event handlers
|
|
415
|
-
const eventHandlers = new Map();
|
|
416
|
-
|
|
417
|
-
const addEventHandler = (event, callback) => {
|
|
418
|
-
if (!eventHandlers.has(event)) {
|
|
419
|
-
eventHandlers.set(event, []);
|
|
420
|
-
}
|
|
421
|
-
eventHandlers.get(event).push(callback);
|
|
422
|
-
};
|
|
423
|
-
|
|
424
|
-
// Activity tracking for title updates
|
|
425
|
-
let idleTimeout = null;
|
|
426
|
-
const IDLE_THRESHOLD = 5000; // 5 seconds
|
|
427
|
-
|
|
428
|
-
// Get test file name for title (use just filename, not full path)
|
|
429
|
-
const testFileName = parsedData?.testFile
|
|
430
|
-
? parsedData.testFile.split('/').pop().split('\\\\').pop()
|
|
431
|
-
: 'TestDriver';
|
|
432
|
-
|
|
433
|
-
const setTitle = (status) => {
|
|
434
|
-
document.title = `[${status}] ${testFileName}`;
|
|
435
|
-
};
|
|
436
|
-
|
|
437
|
-
const resetIdleTimer = () => {
|
|
438
|
-
setTitle("Running");
|
|
439
|
-
if (idleTimeout) clearTimeout(idleTimeout);
|
|
440
|
-
idleTimeout = setTimeout(() => {
|
|
441
|
-
setTitle("Idle");
|
|
442
|
-
}, IDLE_THRESHOLD);
|
|
443
|
-
};
|
|
444
|
-
|
|
445
|
-
ws.addEventListener("message", (message) => {
|
|
446
|
-
resetIdleTimer();
|
|
447
|
-
try {
|
|
448
|
-
const data = JSON.parse(message.data);
|
|
449
|
-
console.log("WebSocket message received:", data);
|
|
450
|
-
const handlers = eventHandlers.get(data.event);
|
|
451
|
-
if (handlers) {
|
|
452
|
-
handlers.forEach((callback) => callback(null, data.data));
|
|
453
|
-
}
|
|
454
|
-
} catch (error) {
|
|
455
|
-
console.error("Error parsing WebSocket message:", error);
|
|
456
|
-
}
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
ws.addEventListener("open", () => {
|
|
460
|
-
console.log("WebSocket connected to TestDriver.ai overlay");
|
|
461
|
-
|
|
462
|
-
// Hide loading screen
|
|
463
|
-
const loadingScreen = document.getElementById("loading-screen");
|
|
464
|
-
loadingScreen.classList.add("hidden");
|
|
465
|
-
|
|
466
|
-
// Show connection status briefly
|
|
467
|
-
document.getElementById("status").textContent = "Connected";
|
|
468
|
-
document.getElementById("status").classList.add("visible");
|
|
469
|
-
setTimeout(() => {
|
|
470
|
-
document.getElementById("status").classList.remove("visible");
|
|
471
|
-
}, 2000);
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
ws.addEventListener("error", (error) => {
|
|
475
|
-
console.error("WebSocket error:", error);
|
|
476
|
-
document.getElementById("status").textContent = "Connection Error";
|
|
477
|
-
document.getElementById("status").classList.add("visible");
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
ws.addEventListener("close", () => {
|
|
481
|
-
console.log("WebSocket disconnected");
|
|
482
|
-
if (idleTimeout) clearTimeout(idleTimeout);
|
|
483
|
-
setTitle("Done");
|
|
484
|
-
document.getElementById("status").textContent = "Disconnected";
|
|
485
|
-
document.getElementById("status").classList.add("visible");
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
// Event handling (same as original)
|
|
489
|
-
const events = {
|
|
490
|
-
mouseClick: "mouse-click",
|
|
491
|
-
mouseMove: "mouse-move",
|
|
492
|
-
screenCapture: {
|
|
493
|
-
start: "screen-capture:start",
|
|
494
|
-
end: "screen-capture:end",
|
|
495
|
-
error: "screen-capture:error",
|
|
496
|
-
},
|
|
497
|
-
interactive: "interactive",
|
|
498
|
-
terminal: {
|
|
499
|
-
stdout: "terminal:stdout",
|
|
500
|
-
stderr: "terminal:stderr",
|
|
501
|
-
},
|
|
502
|
-
matches: {
|
|
503
|
-
show: "matches:show",
|
|
504
|
-
},
|
|
505
|
-
vm: {
|
|
506
|
-
show: "vm:show",
|
|
507
|
-
},
|
|
508
|
-
test: {
|
|
509
|
-
start: "test:start",
|
|
510
|
-
stop: "test:stop",
|
|
511
|
-
success: "test:success",
|
|
512
|
-
error: "test:error",
|
|
513
|
-
},
|
|
514
|
-
error: {
|
|
515
|
-
fatal: "error:fatal",
|
|
516
|
-
general: "error:general",
|
|
517
|
-
sdk: "error:sdk",
|
|
518
|
-
},
|
|
519
|
-
};
|
|
520
|
-
|
|
521
|
-
// Title state handlers for immediate feedback
|
|
522
|
-
addEventHandler(events.test.start, () => {
|
|
523
|
-
if (idleTimeout) clearTimeout(idleTimeout);
|
|
524
|
-
setTitle("Running");
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
addEventHandler(events.test.stop, () => {
|
|
528
|
-
if (idleTimeout) clearTimeout(idleTimeout);
|
|
529
|
-
setTitle("Stopped");
|
|
530
|
-
});
|
|
531
|
-
|
|
532
|
-
addEventHandler(events.test.success, () => {
|
|
533
|
-
if (idleTimeout) clearTimeout(idleTimeout);
|
|
534
|
-
setTitle("Passed");
|
|
535
|
-
});
|
|
536
|
-
|
|
537
|
-
addEventHandler(events.test.error, () => {
|
|
538
|
-
if (idleTimeout) clearTimeout(idleTimeout);
|
|
539
|
-
setTitle("Failed");
|
|
540
|
-
});
|
|
541
|
-
|
|
542
|
-
addEventHandler(events.error.fatal, () => {
|
|
543
|
-
if (idleTimeout) clearTimeout(idleTimeout);
|
|
544
|
-
setTitle("Error");
|
|
545
|
-
});
|
|
546
|
-
|
|
547
|
-
addEventHandler(events.error.sdk, () => {
|
|
548
|
-
if (idleTimeout) clearTimeout(idleTimeout);
|
|
549
|
-
setTitle("Error");
|
|
550
|
-
});
|
|
551
|
-
|
|
552
|
-
const effects = document.getElementById("effects");
|
|
553
|
-
const mouse = document.getElementById("mouse");
|
|
554
|
-
const screenshotElement = document.getElementById("screenshot");
|
|
361
|
+
// Elements
|
|
362
|
+
const iframe = document.getElementById("vm-iframe");
|
|
363
|
+
const overlay = document.getElementById("overlay");
|
|
364
|
+
const loadingScreen = document.getElementById("loading-screen");
|
|
555
365
|
const interactionOverlay = document.getElementById("interaction-overlay");
|
|
556
366
|
|
|
557
|
-
let boundingBoxesTimeout;
|
|
558
|
-
let interactionTimeout;
|
|
559
367
|
let isInteractionEnabled = false;
|
|
560
368
|
|
|
561
|
-
// Show interaction overlay on hover, hide on click, and bring back after 30 seconds
|
|
562
369
|
const showInteractionOverlay = () => {
|
|
563
370
|
if (!isInteractionEnabled) {
|
|
564
371
|
interactionOverlay.classList.add("visible");
|
|
@@ -568,204 +375,78 @@
|
|
|
568
375
|
const hideInteractionOverlay = () => {
|
|
569
376
|
interactionOverlay.classList.remove("visible");
|
|
570
377
|
isInteractionEnabled = true;
|
|
571
|
-
iframe.classList.add("interactive");
|
|
572
|
-
|
|
573
|
-
// Clear any existing timeout
|
|
574
|
-
if (interactionTimeout) {
|
|
575
|
-
clearTimeout(interactionTimeout);
|
|
576
|
-
}
|
|
378
|
+
iframe.classList.add("interactive");
|
|
577
379
|
};
|
|
578
380
|
|
|
579
381
|
const disableInteraction = () => {
|
|
580
382
|
isInteractionEnabled = false;
|
|
581
|
-
iframe.classList.remove("interactive");
|
|
383
|
+
iframe.classList.remove("interactive");
|
|
582
384
|
};
|
|
583
385
|
|
|
584
|
-
// Event listeners for interaction overlay
|
|
585
386
|
overlay.addEventListener("mouseenter", showInteractionOverlay);
|
|
586
387
|
overlay.addEventListener("mouseleave", () => {
|
|
587
388
|
if (!isInteractionEnabled) {
|
|
588
389
|
interactionOverlay.classList.remove("visible");
|
|
589
390
|
}
|
|
590
391
|
});
|
|
591
|
-
|
|
592
392
|
interactionOverlay.addEventListener("click", hideInteractionOverlay);
|
|
593
393
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
box.remove();
|
|
598
|
-
});
|
|
599
|
-
|
|
600
|
-
// Add new boxes
|
|
601
|
-
boxes.forEach((box) => {
|
|
602
|
-
const boxElement = document.createElement("div");
|
|
603
|
-
boxElement.className = "bounding-box";
|
|
604
|
-
boxElement.style.left = toCss(box.x);
|
|
605
|
-
boxElement.style.top = toCss(box.y + topBarOffset);
|
|
606
|
-
boxElement.style.width = toCss(box.width);
|
|
607
|
-
boxElement.style.height = toCss(box.height);
|
|
608
|
-
effects.appendChild(boxElement);
|
|
609
|
-
});
|
|
610
|
-
};
|
|
611
|
-
// Screen capture event handlers
|
|
612
|
-
addEventHandler(events.screenCapture.start, (event, data) => {
|
|
613
|
-
if (data?.silent) return;
|
|
614
|
-
screenshotElement.style.opacity = 0;
|
|
615
|
-
});
|
|
616
|
-
|
|
617
|
-
addEventHandler(events.screenCapture.error, (event, data) => {
|
|
618
|
-
if (data?.silent) return;
|
|
619
|
-
screenshotElement.style.opacity = 1;
|
|
620
|
-
});
|
|
621
|
-
|
|
622
|
-
addEventHandler(events.screenCapture.end, (event, data) => {
|
|
623
|
-
if (data?.silent) return;
|
|
624
|
-
screenshotElement.classList.remove("screenshot");
|
|
625
|
-
// Force reflow
|
|
626
|
-
void screenshotElement.offsetWidth;
|
|
627
|
-
screenshotElement.classList.add("screenshot");
|
|
394
|
+
window.addEventListener("blur", () => {
|
|
395
|
+
showInteractionOverlay();
|
|
396
|
+
disableInteraction();
|
|
628
397
|
});
|
|
629
398
|
|
|
630
|
-
//
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
}
|
|
399
|
+
// Set title from test file name
|
|
400
|
+
const testFileName = parsedData?.testFile
|
|
401
|
+
? parsedData.testFile.split("/").pop().split("\\").pop()
|
|
402
|
+
: "TestDriver";
|
|
403
|
+
document.title = `${testFileName} - Debugger`;
|
|
635
404
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
mouse.style.marginLeft = toCss(x);
|
|
640
|
-
mouse.style.marginTop = toCss(y + topBarOffset);
|
|
641
|
-
// Reset class so animation can restart
|
|
642
|
-
mouse.setAttribute("class", "mouse");
|
|
643
|
-
// Force reflow
|
|
644
|
-
void mouse.offsetWidth;
|
|
645
|
-
mouse.classList.add(`${click}-click`);
|
|
646
|
-
},
|
|
647
|
-
);
|
|
648
|
-
|
|
649
|
-
// Matches and VM event handlers
|
|
650
|
-
addEventHandler(events.matches.show, (event, closeMatches = []) => {
|
|
651
|
-
if (boundingBoxesTimeout) clearTimeout(boundingBoxesTimeout);
|
|
652
|
-
drawBoxes(closeMatches);
|
|
653
|
-
boundingBoxesTimeout = setTimeout(() => drawBoxes([]), 10000);
|
|
654
|
-
});
|
|
405
|
+
// Embed the VNC URL in the iframe
|
|
406
|
+
iframe.style.display = "block";
|
|
407
|
+
iframe.src = parsedData.url;
|
|
655
408
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
409
|
+
// Hide loading screen once iframe loads
|
|
410
|
+
iframe.addEventListener("load", () => {
|
|
411
|
+
loadingScreen.classList.add("hidden");
|
|
659
412
|
});
|
|
413
|
+
// Fallback: hide loading screen after 5s even if load event doesn't fire
|
|
414
|
+
setTimeout(() => loadingScreen.classList.add("hidden"), 5000);
|
|
660
415
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
});
|
|
416
|
+
// Resolution from session data
|
|
417
|
+
const overlayWidth = parsedData.resolution?.[0] || 1366;
|
|
418
|
+
const overlayHeight = parsedData.resolution?.[1] || 768;
|
|
665
419
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
return `${size}px`;
|
|
669
|
-
}
|
|
670
|
-
return size;
|
|
671
|
-
};
|
|
420
|
+
iframe.style.width = overlayWidth + "px";
|
|
421
|
+
iframe.style.height = overlayHeight + "px";
|
|
672
422
|
|
|
673
|
-
// Throttle
|
|
423
|
+
// Throttle helper
|
|
674
424
|
function throttle(fn, wait) {
|
|
675
|
-
let
|
|
425
|
+
let last = 0;
|
|
676
426
|
return function (...args) {
|
|
677
427
|
const now = Date.now();
|
|
678
|
-
if (now -
|
|
679
|
-
lastTime = now;
|
|
680
|
-
fn.apply(this, args);
|
|
681
|
-
}
|
|
428
|
+
if (now - last >= wait) { last = now; fn.apply(this, args); }
|
|
682
429
|
};
|
|
683
430
|
}
|
|
684
431
|
|
|
432
|
+
// Responsive: scale and center the overlay to fit the window
|
|
685
433
|
const resizeOverlay = () => {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
"resolution:",
|
|
699
|
-
JSON.stringify({
|
|
700
|
-
width: overlayWidth,
|
|
701
|
-
height: overlayHeight,
|
|
702
|
-
}),
|
|
703
|
-
);
|
|
704
|
-
|
|
705
|
-
console.log(
|
|
706
|
-
"window:",
|
|
707
|
-
JSON.stringify({
|
|
708
|
-
width: windowWidth,
|
|
709
|
-
height: windowHeight,
|
|
710
|
-
}),
|
|
711
|
-
);
|
|
712
|
-
const overlay = document.getElementById("overlay");
|
|
713
|
-
if (scale < 1) {
|
|
714
|
-
overlay.style.transform = `scale(${scale})`;
|
|
715
|
-
} else {
|
|
716
|
-
overlay.style.transform = "none";
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
// get the new width and height after scaling
|
|
720
|
-
const scaledWidth = overlayWidth * scale;
|
|
721
|
-
const scaledHeight = overlayHeight * scale;
|
|
722
|
-
|
|
723
|
-
console.log("scaledWidth", JSON.stringify(scaledWidth));
|
|
724
|
-
console.log("scaledHeight", JSON.stringify(scaledHeight));
|
|
725
|
-
|
|
726
|
-
// use the scaled width and height to center the overlay
|
|
727
|
-
overlay.style.position = "absolute";
|
|
728
|
-
if (scale < 1) {
|
|
729
|
-
// When scaling down, use the original dimensions and let CSS transform handle the scaling
|
|
730
|
-
overlay.style.width = `${overlayWidth}px`;
|
|
731
|
-
overlay.style.height = `${overlayHeight}px`;
|
|
732
|
-
overlay.style.left = `${(windowWidth - scaledWidth) / 2}px`;
|
|
733
|
-
overlay.style.top = `${(windowHeight - scaledHeight) / 2}px`;
|
|
734
|
-
} else {
|
|
735
|
-
// When no scaling is needed, just center the overlay at its original size
|
|
736
|
-
overlay.style.width = `${overlayWidth}px`;
|
|
737
|
-
overlay.style.height = `${overlayHeight}px`;
|
|
738
|
-
overlay.style.left = `${(windowWidth - overlayWidth) / 2}px`;
|
|
739
|
-
overlay.style.top = `${(windowHeight - overlayHeight) / 2}px`;
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
console.log(
|
|
743
|
-
"left,top",
|
|
744
|
-
JSON.stringify({
|
|
745
|
-
left:
|
|
746
|
-
scale < 1
|
|
747
|
-
? (windowWidth - scaledWidth) / 2
|
|
748
|
-
: (windowWidth - overlayWidth) / 2,
|
|
749
|
-
top:
|
|
750
|
-
scale < 1
|
|
751
|
-
? (windowHeight - scaledHeight) / 2
|
|
752
|
-
: (windowHeight - overlayHeight) / 2,
|
|
753
|
-
}),
|
|
754
|
-
);
|
|
755
|
-
}
|
|
434
|
+
const ww = window.innerWidth;
|
|
435
|
+
const wh = window.innerHeight;
|
|
436
|
+
const scale = Math.min(ww / overlayWidth, wh / overlayHeight, 1);
|
|
437
|
+
const sw = overlayWidth * scale;
|
|
438
|
+
const sh = overlayHeight * scale;
|
|
439
|
+
|
|
440
|
+
overlay.style.width = overlayWidth + "px";
|
|
441
|
+
overlay.style.height = overlayHeight + "px";
|
|
442
|
+
overlay.style.position = "absolute";
|
|
443
|
+
overlay.style.transform = scale < 1 ? `scale(${scale})` : "none";
|
|
444
|
+
overlay.style.left = `${(ww - sw) / 2}px`;
|
|
445
|
+
overlay.style.top = `${(wh - sh) / 2}px`;
|
|
756
446
|
};
|
|
757
|
-
resizeOverlay();
|
|
758
447
|
|
|
759
|
-
|
|
448
|
+
resizeOverlay();
|
|
760
449
|
window.addEventListener("resize", throttle(resizeOverlay, 100));
|
|
761
|
-
|
|
762
|
-
setInterval(resizeOverlay, 1000);
|
|
763
|
-
|
|
764
|
-
// Handle window blur/focus for screen locking
|
|
765
|
-
window.addEventListener("blur", () => {
|
|
766
|
-
showInteractionOverlay();
|
|
767
|
-
disableInteraction();
|
|
768
|
-
});
|
|
769
450
|
</script>
|
|
770
451
|
</body>
|
|
771
452
|
</html>
|
|
@@ -36,11 +36,16 @@ function parseFrontmatter(content) {
|
|
|
36
36
|
return { frontmatter, body };
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
// Get skill name from filename
|
|
39
|
+
// Get skill name from filename (used in SKILL.md frontmatter)
|
|
40
40
|
function getSkillName(filename) {
|
|
41
41
|
return `testdriver:${filename.replace(".mdx", "")}`;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
// Get directory name from filename (Windows-compatible, uses hyphen instead of colon)
|
|
45
|
+
function getDirName(filename) {
|
|
46
|
+
return `testdriver-${filename.replace(".mdx", "")}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
44
49
|
// Generate SKILL.md content
|
|
45
50
|
function generateSkillContent(filename, frontmatter, body) {
|
|
46
51
|
const skillName = getSkillName(filename);
|
|
@@ -76,7 +81,7 @@ function processFiles() {
|
|
|
76
81
|
for (const file of mdxFiles) {
|
|
77
82
|
const filePath = path.join(DOCS_DIR, file);
|
|
78
83
|
const skillName = getSkillName(file);
|
|
79
|
-
const outputDir = path.join(OUTPUT_DIR,
|
|
84
|
+
const outputDir = path.join(OUTPUT_DIR, getDirName(file));
|
|
80
85
|
const outputPath = path.join(outputDir, "SKILL.md");
|
|
81
86
|
|
|
82
87
|
try {
|
package/docs/v7/quickstart.mdx
CHANGED
|
@@ -81,8 +81,6 @@ TestDriver makes it easy to write automated computer-use tests for web browsers,
|
|
|
81
81
|
test: {
|
|
82
82
|
testTimeout: 900000,
|
|
83
83
|
hookTimeout: 900000,
|
|
84
|
-
disableConsoleIntercept: true,
|
|
85
|
-
maxConcurrency: 1, // this should match your plan's concurrency limit
|
|
86
84
|
reporters: [
|
|
87
85
|
'default',
|
|
88
86
|
TestDriver()
|
|
@@ -76,7 +76,7 @@ class BaseCommand extends Command {
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
let isConnected = false;
|
|
79
|
-
const debugMode = process.env.VERBOSE || process.env.
|
|
79
|
+
const debugMode = process.env.VERBOSE || process.env.TD_DEBUG;
|
|
80
80
|
|
|
81
81
|
// Use pattern matching for log events, but skip log:Debug unless debug mode is enabled
|
|
82
82
|
this.agent.emitter.on("log:*", (message) => {
|
|
@@ -586,7 +586,7 @@ export async function cleanupTestDriver(testdriver) {
|
|
|
586
586
|
try {
|
|
587
587
|
const dashcamUrl = await testdriver.dashcam.stop();
|
|
588
588
|
const debugMode =
|
|
589
|
-
process.env.VERBOSE || process.env.
|
|
589
|
+
process.env.VERBOSE || process.env.TD_DEBUG;
|
|
590
590
|
if (debugMode) {
|
|
591
591
|
console.log("🎥 Dashcam URL:", dashcamUrl);
|
|
592
592
|
}
|
|
@@ -1243,6 +1243,9 @@ class TestDriverReporter {
|
|
|
1243
1243
|
* @returns {string} The corresponding web console URL
|
|
1244
1244
|
*/
|
|
1245
1245
|
function getConsoleUrl(apiRoot) {
|
|
1246
|
+
// Allow explicit override via env (e.g. VITE_DOMAIN from .env)
|
|
1247
|
+
if (process.env.VITE_DOMAIN) return process.env.VITE_DOMAIN;
|
|
1248
|
+
|
|
1246
1249
|
if (!apiRoot) return "https://console.testdriver.ai";
|
|
1247
1250
|
|
|
1248
1251
|
// Production: API on render.com -> Console on testdriver.ai
|
|
@@ -1251,13 +1254,11 @@ function getConsoleUrl(apiRoot) {
|
|
|
1251
1254
|
}
|
|
1252
1255
|
|
|
1253
1256
|
// Local development: API on localhost:1337 -> Web on localhost:3001
|
|
1254
|
-
if (apiRoot.includes("ngrok.io")) {
|
|
1257
|
+
if (apiRoot.includes("ngrok.io") || apiRoot.includes("trycloudflare.com") || apiRoot.includes("localhost")) {
|
|
1255
1258
|
return `http://localhost:3001`;
|
|
1256
1259
|
}
|
|
1257
1260
|
|
|
1258
|
-
//
|
|
1259
|
-
// For ngrok, the API and web might be on same domain or user needs to configure
|
|
1260
|
-
// Return as-is since we can't reliably determine the mapping
|
|
1261
|
+
// Other tunnels or unknown hosts: return as-is
|
|
1261
1262
|
return apiRoot;
|
|
1262
1263
|
}
|
|
1263
1264
|
|