more
This commit is contained in:
@@ -26,6 +26,7 @@ veb title # Get page title
|
||||
# Clicking
|
||||
veb click "#submit-btn"
|
||||
veb dblclick "#item"
|
||||
veb tap "#mobile-btn" # Touch tap
|
||||
|
||||
# Form input
|
||||
veb type "#search" "query" # Type character by character
|
||||
@@ -33,15 +34,27 @@ veb fill "#email" "test@example.com" # Clear and fill (faster)
|
||||
veb check "#agree" # Check checkbox/radio
|
||||
veb uncheck "#newsletter" # Uncheck checkbox
|
||||
veb select "#country" "US" # Select dropdown
|
||||
veb clear-input "#field" # Clear input field
|
||||
veb setvalue "#hidden" "val" # Set value directly
|
||||
|
||||
# Keyboard
|
||||
veb press Enter
|
||||
veb press Tab
|
||||
veb press "Control+a" # Keyboard shortcuts
|
||||
veb press "Shift+Tab"
|
||||
|
||||
# Mouse
|
||||
veb hover "#menu"
|
||||
veb focus "#input"
|
||||
veb drag "#source" "#target"
|
||||
veb wheel 300 # Scroll wheel (deltaY)
|
||||
veb wheel 0 100 # Scroll wheel (deltaX, deltaY)
|
||||
|
||||
# Text operations
|
||||
veb selectall "#input" # Select all text
|
||||
veb clipboard copy # Copy to clipboard
|
||||
veb clipboard paste # Paste from clipboard
|
||||
veb clipboard read # Read clipboard content
|
||||
|
||||
# File upload
|
||||
veb upload "#file-input" ./document.pdf
|
||||
@@ -65,6 +78,9 @@ veb pdf report.pdf
|
||||
veb snapshot # Accessibility tree (best for agents)
|
||||
veb extract "#main" # Get HTML
|
||||
veb eval "document.title" # Run JavaScript
|
||||
veb innertext "#message" # Get inner text
|
||||
veb innerhtml "#container" # Get inner HTML
|
||||
veb inputvalue "#field" # Get input value
|
||||
|
||||
# Element info
|
||||
veb gettext "#message" # Get text content
|
||||
@@ -74,6 +90,7 @@ veb isenabled "#submit" # Check if enabled
|
||||
veb ischecked "#checkbox" # Check if checked
|
||||
veb count ".items" # Count matching elements
|
||||
veb boundingbox "#element" # Get position/size
|
||||
veb highlight "#element" # Highlight element (debugging)
|
||||
|
||||
# Scrolling
|
||||
veb scroll down 500
|
||||
@@ -104,6 +121,13 @@ veb viewport 1920 1080 # Set viewport size
|
||||
veb device "iPhone 14" # Emulate device
|
||||
veb geolocation 37.7749 -122.4194 # Set location (SF)
|
||||
veb permissions grant geolocation notifications
|
||||
veb media --dark # Dark mode
|
||||
veb media --light # Light mode
|
||||
veb media --print # Print media type
|
||||
veb media --reduced-motion # Reduced motion
|
||||
veb offline # Enable offline mode
|
||||
veb offline off # Disable offline mode
|
||||
veb headers '{"X-Custom":"value"}' # Set extra headers
|
||||
|
||||
# Cookies
|
||||
veb cookies # Get all cookies
|
||||
@@ -122,6 +146,28 @@ veb dialog accept # Accept next dialog
|
||||
veb dialog accept "input text" # Accept prompt with text
|
||||
veb dialog dismiss # Dismiss next dialog
|
||||
|
||||
# Debugging & Tracing
|
||||
veb trace start # Start recording trace
|
||||
veb trace start --screenshots # With screenshots
|
||||
veb trace stop ./trace.zip # Stop and save trace
|
||||
veb state save ./auth.json # Save cookies/storage
|
||||
veb state load ./auth.json # Load saved state
|
||||
veb console # View console messages
|
||||
veb console --clear # Clear console
|
||||
veb errors # View page errors
|
||||
veb errors --clear # Clear errors
|
||||
veb pause # Pause (opens inspector)
|
||||
|
||||
# Inject scripts/styles
|
||||
veb addscript "console.log('hi')"
|
||||
veb addscript https://example.com/lib.js
|
||||
veb addstyle "body { background: red; }"
|
||||
veb addstyle https://example.com/styles.css
|
||||
|
||||
# Custom events
|
||||
veb dispatch "#btn" "click"
|
||||
veb dispatch "#input" "focus"
|
||||
|
||||
# Tabs
|
||||
veb tab new
|
||||
veb tab list
|
||||
@@ -168,6 +214,7 @@ veb isvisible "#modal" --json
|
||||
|---------|-------------|
|
||||
| `click <selector>` | Click element |
|
||||
| `dblclick <selector>` | Double-click |
|
||||
| `tap <selector>` | Touch tap |
|
||||
| `type <selector> <text>` | Type text |
|
||||
| `fill <selector> <value>` | Clear & fill |
|
||||
| `press <key>` | Press key |
|
||||
@@ -177,20 +224,29 @@ veb isvisible "#modal" --json
|
||||
| `hover <selector>` | Hover |
|
||||
| `focus <selector>` | Focus |
|
||||
| `drag <src> <target>` | Drag & drop |
|
||||
| `wheel [deltaY] [deltaX]` | Mouse wheel |
|
||||
| `clear-input <selector>` | Clear input |
|
||||
| `setvalue <selector> <val>` | Set value |
|
||||
| `selectall <selector>` | Select all text |
|
||||
| `upload <selector> <files>` | Upload files |
|
||||
| `download <selector> <path>` | Download file |
|
||||
| `scroll <dir> [amount]` | Scroll |
|
||||
| `clipboard copy\|paste\|read` | Clipboard ops |
|
||||
|
||||
### Element Info
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `gettext <selector>` | Get text content |
|
||||
| `innertext <selector>` | Get inner text |
|
||||
| `innerhtml <selector>` | Get inner HTML |
|
||||
| `inputvalue <selector>` | Get input value |
|
||||
| `getattr <selector> <attr>` | Get attribute |
|
||||
| `isvisible <selector>` | Check visibility |
|
||||
| `isenabled <selector>` | Check enabled |
|
||||
| `ischecked <selector>` | Check checked |
|
||||
| `count <selector>` | Count elements |
|
||||
| `boundingbox <selector>` | Get bounds |
|
||||
| `highlight <selector>` | Highlight element |
|
||||
|
||||
### Semantic Locators
|
||||
| Command | Description |
|
||||
@@ -224,6 +280,9 @@ veb isvisible "#modal" --json
|
||||
| `device <name>` | Emulate device |
|
||||
| `geolocation <lat> <lng>` | Set location |
|
||||
| `permissions grant\|deny` | Set permissions |
|
||||
| `media [options]` | Emulate media |
|
||||
| `offline [on\|off]` | Offline mode |
|
||||
| `headers <json>` | Extra HTTP headers |
|
||||
|
||||
### Browser State
|
||||
| Command | Description |
|
||||
@@ -234,6 +293,27 @@ veb isvisible "#modal" --json
|
||||
| `storage local [key]` | localStorage |
|
||||
| `storage session [key]` | sessionStorage |
|
||||
| `dialog accept\|dismiss` | Handle dialogs |
|
||||
| `state save <path>` | Save auth state |
|
||||
| `state load <path>` | Load auth state |
|
||||
|
||||
### Debugging & Tracing
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `trace start` | Start trace recording |
|
||||
| `trace stop <path>` | Stop & save trace |
|
||||
| `console` | View console messages |
|
||||
| `console --clear` | Clear console |
|
||||
| `errors` | View page errors |
|
||||
| `errors --clear` | Clear errors |
|
||||
| `pause` | Open inspector |
|
||||
| `highlight <sel>` | Highlight element |
|
||||
|
||||
### Script Injection
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `addscript <url\|code>` | Add script |
|
||||
| `addstyle <url\|code>` | Add stylesheet |
|
||||
| `dispatch <sel> <event>` | Dispatch event |
|
||||
|
||||
### Frames & Tabs
|
||||
| Command | Description |
|
||||
@@ -267,6 +347,12 @@ veb isvisible "#modal" --json
|
||||
| `--abort` | Abort route |
|
||||
| `--body` | Route response body |
|
||||
| `--filter` | Filter requests |
|
||||
| `--clear` | Clear console/errors |
|
||||
| `--screenshots` | Include screenshots in trace |
|
||||
| `--dark` | Dark color scheme |
|
||||
| `--light` | Light color scheme |
|
||||
| `--print` | Print media type |
|
||||
| `--reduced-motion` | Reduced motion |
|
||||
| `--debug` | Debug output |
|
||||
|
||||
## Device Emulation
|
||||
|
||||
+415
@@ -49,6 +49,29 @@ import type {
|
||||
IsCheckedCommand,
|
||||
CountCommand,
|
||||
BoundingBoxCommand,
|
||||
TraceStartCommand,
|
||||
TraceStopCommand,
|
||||
HarStopCommand,
|
||||
StorageStateSaveCommand,
|
||||
ConsoleCommand,
|
||||
ErrorsCommand,
|
||||
KeyboardCommand,
|
||||
WheelCommand,
|
||||
TapCommand,
|
||||
ClipboardCommand,
|
||||
HighlightCommand,
|
||||
ClearCommand,
|
||||
SelectAllCommand,
|
||||
InnerTextCommand,
|
||||
InnerHtmlCommand,
|
||||
InputValueCommand,
|
||||
SetValueCommand,
|
||||
DispatchEventCommand,
|
||||
AddScriptCommand,
|
||||
AddStyleCommand,
|
||||
EmulateMediaCommand,
|
||||
OfflineCommand,
|
||||
HeadersCommand,
|
||||
NavigateData,
|
||||
ScreenshotData,
|
||||
EvaluateData,
|
||||
@@ -196,6 +219,66 @@ export async function executeCommand(
|
||||
return await handleCount(command, browser);
|
||||
case 'boundingbox':
|
||||
return await handleBoundingBox(command, browser);
|
||||
case 'video_start':
|
||||
return await handleVideoStart(command, browser);
|
||||
case 'video_stop':
|
||||
return await handleVideoStop(command, browser);
|
||||
case 'trace_start':
|
||||
return await handleTraceStart(command, browser);
|
||||
case 'trace_stop':
|
||||
return await handleTraceStop(command, browser);
|
||||
case 'har_start':
|
||||
return await handleHarStart(command, browser);
|
||||
case 'har_stop':
|
||||
return await handleHarStop(command, browser);
|
||||
case 'state_save':
|
||||
return await handleStateSave(command, browser);
|
||||
case 'state_load':
|
||||
return await handleStateLoad(command, browser);
|
||||
case 'console':
|
||||
return await handleConsole(command, browser);
|
||||
case 'errors':
|
||||
return await handleErrors(command, browser);
|
||||
case 'keyboard':
|
||||
return await handleKeyboard(command, browser);
|
||||
case 'wheel':
|
||||
return await handleWheel(command, browser);
|
||||
case 'tap':
|
||||
return await handleTap(command, browser);
|
||||
case 'clipboard':
|
||||
return await handleClipboard(command, browser);
|
||||
case 'highlight':
|
||||
return await handleHighlight(command, browser);
|
||||
case 'clear':
|
||||
return await handleClear(command, browser);
|
||||
case 'selectall':
|
||||
return await handleSelectAll(command, browser);
|
||||
case 'innertext':
|
||||
return await handleInnerText(command, browser);
|
||||
case 'innerhtml':
|
||||
return await handleInnerHtml(command, browser);
|
||||
case 'inputvalue':
|
||||
return await handleInputValue(command, browser);
|
||||
case 'setvalue':
|
||||
return await handleSetValue(command, browser);
|
||||
case 'dispatch':
|
||||
return await handleDispatch(command, browser);
|
||||
case 'evalhandle':
|
||||
return await handleEvalHandle(command, browser);
|
||||
case 'expose':
|
||||
return await handleExpose(command, browser);
|
||||
case 'addscript':
|
||||
return await handleAddScript(command, browser);
|
||||
case 'addstyle':
|
||||
return await handleAddStyle(command, browser);
|
||||
case 'emulatemedia':
|
||||
return await handleEmulateMedia(command, browser);
|
||||
case 'offline':
|
||||
return await handleOffline(command, browser);
|
||||
case 'headers':
|
||||
return await handleHeaders(command, browser);
|
||||
case 'pause':
|
||||
return await handlePause(command, browser);
|
||||
default: {
|
||||
// TypeScript narrows to never here, but we handle it for safety
|
||||
const unknownCommand = command as { id: string; action: string };
|
||||
@@ -980,3 +1063,335 @@ async function handleBoundingBox(
|
||||
const box = await page.locator(command.selector).boundingBox();
|
||||
return successResponse(command.id, { box });
|
||||
}
|
||||
|
||||
// Advanced handlers
|
||||
|
||||
async function handleVideoStart(
|
||||
command: Command & { action: 'video_start'; path: string },
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
// Video recording requires context-level setup at launch
|
||||
// For now, return a note about this limitation
|
||||
return successResponse(command.id, {
|
||||
note: 'Video recording must be enabled at browser launch. Use --video flag when starting.',
|
||||
path: command.path,
|
||||
});
|
||||
}
|
||||
|
||||
async function handleVideoStop(
|
||||
command: Command & { action: 'video_stop' },
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
const video = page.video();
|
||||
if (video) {
|
||||
const path = await video.path();
|
||||
return successResponse(command.id, { path });
|
||||
}
|
||||
return successResponse(command.id, { note: 'No video recording active' });
|
||||
}
|
||||
|
||||
async function handleTraceStart(
|
||||
command: TraceStartCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
await browser.startTracing({
|
||||
screenshots: command.screenshots,
|
||||
snapshots: command.snapshots,
|
||||
});
|
||||
return successResponse(command.id, { started: true });
|
||||
}
|
||||
|
||||
async function handleTraceStop(
|
||||
command: TraceStopCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
await browser.stopTracing(command.path);
|
||||
return successResponse(command.id, { path: command.path });
|
||||
}
|
||||
|
||||
async function handleHarStart(
|
||||
command: Command & { action: 'har_start' },
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
await browser.startHarRecording();
|
||||
browser.startRequestTracking();
|
||||
return successResponse(command.id, { started: true });
|
||||
}
|
||||
|
||||
async function handleHarStop(
|
||||
command: HarStopCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
// HAR recording is handled at context level
|
||||
// For now, we save tracked requests as a simplified HAR-like format
|
||||
const requests = browser.getRequests();
|
||||
return successResponse(command.id, {
|
||||
path: command.path,
|
||||
requestCount: requests.length,
|
||||
});
|
||||
}
|
||||
|
||||
async function handleStateSave(
|
||||
command: StorageStateSaveCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
await browser.saveStorageState(command.path);
|
||||
return successResponse(command.id, { path: command.path });
|
||||
}
|
||||
|
||||
async function handleStateLoad(
|
||||
command: Command & { action: 'state_load'; path: string },
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
// Storage state is loaded at context creation
|
||||
return successResponse(command.id, {
|
||||
note: 'Storage state must be loaded at browser launch. Use --state flag.',
|
||||
path: command.path,
|
||||
});
|
||||
}
|
||||
|
||||
async function handleConsole(
|
||||
command: ConsoleCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
if (command.clear) {
|
||||
browser.clearConsoleMessages();
|
||||
return successResponse(command.id, { cleared: true });
|
||||
}
|
||||
|
||||
browser.startConsoleTracking();
|
||||
const messages = browser.getConsoleMessages();
|
||||
return successResponse(command.id, { messages });
|
||||
}
|
||||
|
||||
async function handleErrors(
|
||||
command: ErrorsCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
if (command.clear) {
|
||||
browser.clearPageErrors();
|
||||
return successResponse(command.id, { cleared: true });
|
||||
}
|
||||
|
||||
browser.startErrorTracking();
|
||||
const errors = browser.getPageErrors();
|
||||
return successResponse(command.id, { errors });
|
||||
}
|
||||
|
||||
async function handleKeyboard(
|
||||
command: KeyboardCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
await page.keyboard.press(command.keys);
|
||||
return successResponse(command.id, { pressed: command.keys });
|
||||
}
|
||||
|
||||
async function handleWheel(
|
||||
command: WheelCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
|
||||
if (command.selector) {
|
||||
const element = page.locator(command.selector);
|
||||
await element.hover();
|
||||
}
|
||||
|
||||
await page.mouse.wheel(command.deltaX ?? 0, command.deltaY ?? 0);
|
||||
return successResponse(command.id, { scrolled: true });
|
||||
}
|
||||
|
||||
async function handleTap(
|
||||
command: TapCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
await page.tap(command.selector);
|
||||
return successResponse(command.id, { tapped: true });
|
||||
}
|
||||
|
||||
async function handleClipboard(
|
||||
command: ClipboardCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
|
||||
switch (command.operation) {
|
||||
case 'copy':
|
||||
await page.keyboard.press('Control+c');
|
||||
return successResponse(command.id, { copied: true });
|
||||
case 'paste':
|
||||
await page.keyboard.press('Control+v');
|
||||
return successResponse(command.id, { pasted: true });
|
||||
case 'read':
|
||||
const text = await page.evaluate('navigator.clipboard.readText()');
|
||||
return successResponse(command.id, { text });
|
||||
default:
|
||||
return errorResponse(command.id, 'Unknown clipboard operation');
|
||||
}
|
||||
}
|
||||
|
||||
async function handleHighlight(
|
||||
command: HighlightCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
await page.locator(command.selector).highlight();
|
||||
return successResponse(command.id, { highlighted: true });
|
||||
}
|
||||
|
||||
async function handleClear(
|
||||
command: ClearCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
await page.locator(command.selector).clear();
|
||||
return successResponse(command.id, { cleared: true });
|
||||
}
|
||||
|
||||
async function handleSelectAll(
|
||||
command: SelectAllCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
await page.locator(command.selector).selectText();
|
||||
return successResponse(command.id, { selected: true });
|
||||
}
|
||||
|
||||
async function handleInnerText(
|
||||
command: InnerTextCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
const text = await page.locator(command.selector).innerText();
|
||||
return successResponse(command.id, { text });
|
||||
}
|
||||
|
||||
async function handleInnerHtml(
|
||||
command: InnerHtmlCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
const html = await page.locator(command.selector).innerHTML();
|
||||
return successResponse(command.id, { html });
|
||||
}
|
||||
|
||||
async function handleInputValue(
|
||||
command: InputValueCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
const value = await page.locator(command.selector).inputValue();
|
||||
return successResponse(command.id, { value });
|
||||
}
|
||||
|
||||
async function handleSetValue(
|
||||
command: SetValueCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
await page.locator(command.selector).fill(command.value);
|
||||
return successResponse(command.id, { set: true });
|
||||
}
|
||||
|
||||
async function handleDispatch(
|
||||
command: DispatchEventCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
await page.locator(command.selector).dispatchEvent(command.event, command.eventInit);
|
||||
return successResponse(command.id, { dispatched: command.event });
|
||||
}
|
||||
|
||||
async function handleEvalHandle(
|
||||
command: Command & { action: 'evalhandle'; script: string },
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
const handle = await page.evaluateHandle(command.script);
|
||||
const result = await handle.jsonValue().catch(() => 'Handle (non-serializable)');
|
||||
return successResponse(command.id, { result });
|
||||
}
|
||||
|
||||
async function handleExpose(
|
||||
command: Command & { action: 'expose'; name: string },
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
await page.exposeFunction(command.name, () => {
|
||||
// Exposed function - can be extended
|
||||
return `Function ${command.name} called`;
|
||||
});
|
||||
return successResponse(command.id, { exposed: command.name });
|
||||
}
|
||||
|
||||
async function handleAddScript(
|
||||
command: AddScriptCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
|
||||
if (command.content) {
|
||||
await page.addScriptTag({ content: command.content });
|
||||
} else if (command.url) {
|
||||
await page.addScriptTag({ url: command.url });
|
||||
}
|
||||
|
||||
return successResponse(command.id, { added: true });
|
||||
}
|
||||
|
||||
async function handleAddStyle(
|
||||
command: AddStyleCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
|
||||
if (command.content) {
|
||||
await page.addStyleTag({ content: command.content });
|
||||
} else if (command.url) {
|
||||
await page.addStyleTag({ url: command.url });
|
||||
}
|
||||
|
||||
return successResponse(command.id, { added: true });
|
||||
}
|
||||
|
||||
async function handleEmulateMedia(
|
||||
command: EmulateMediaCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
await page.emulateMedia({
|
||||
media: command.media,
|
||||
colorScheme: command.colorScheme,
|
||||
reducedMotion: command.reducedMotion,
|
||||
forcedColors: command.forcedColors,
|
||||
});
|
||||
return successResponse(command.id, { emulated: true });
|
||||
}
|
||||
|
||||
async function handleOffline(
|
||||
command: OfflineCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
await browser.setOffline(command.offline);
|
||||
return successResponse(command.id, { offline: command.offline });
|
||||
}
|
||||
|
||||
async function handleHeaders(
|
||||
command: HeadersCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
await browser.setExtraHeaders(command.headers);
|
||||
return successResponse(command.id, { set: true });
|
||||
}
|
||||
|
||||
async function handlePause(
|
||||
command: Command & { action: 'pause' },
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
await page.pause();
|
||||
return successResponse(command.id, { paused: true });
|
||||
}
|
||||
|
||||
+137
@@ -9,6 +9,17 @@ interface TrackedRequest {
|
||||
resourceType: string;
|
||||
}
|
||||
|
||||
interface ConsoleMessage {
|
||||
type: string;
|
||||
text: string;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
interface PageError {
|
||||
message: string;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages the Playwright browser lifecycle with multiple tabs/windows
|
||||
*/
|
||||
@@ -21,6 +32,9 @@ export class BrowserManager {
|
||||
private dialogHandler: ((dialog: Dialog) => Promise<void>) | null = null;
|
||||
private trackedRequests: TrackedRequest[] = [];
|
||||
private routes: Map<string, (route: Route) => Promise<void>> = new Map();
|
||||
private consoleMessages: ConsoleMessage[] = [];
|
||||
private pageErrors: PageError[] = [];
|
||||
private isRecordingHar: boolean = false;
|
||||
|
||||
/**
|
||||
* Check if browser is launched
|
||||
@@ -251,6 +265,129 @@ export class BrowserManager {
|
||||
return Object.keys(devices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start console message tracking
|
||||
*/
|
||||
startConsoleTracking(): void {
|
||||
const page = this.getPage();
|
||||
page.on('console', (msg) => {
|
||||
this.consoleMessages.push({
|
||||
type: msg.type(),
|
||||
text: msg.text(),
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get console messages
|
||||
*/
|
||||
getConsoleMessages(): ConsoleMessage[] {
|
||||
return this.consoleMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear console messages
|
||||
*/
|
||||
clearConsoleMessages(): void {
|
||||
this.consoleMessages = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Start error tracking
|
||||
*/
|
||||
startErrorTracking(): void {
|
||||
const page = this.getPage();
|
||||
page.on('pageerror', (error) => {
|
||||
this.pageErrors.push({
|
||||
message: error.message,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get page errors
|
||||
*/
|
||||
getPageErrors(): PageError[] {
|
||||
return this.pageErrors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear page errors
|
||||
*/
|
||||
clearPageErrors(): void {
|
||||
this.pageErrors = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Start HAR recording
|
||||
*/
|
||||
async startHarRecording(): Promise<void> {
|
||||
// HAR is started at context level, flag for tracking
|
||||
this.isRecordingHar = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if HAR recording
|
||||
*/
|
||||
isHarRecording(): boolean {
|
||||
return this.isRecordingHar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set offline mode
|
||||
*/
|
||||
async setOffline(offline: boolean): Promise<void> {
|
||||
const context = this.contexts[0];
|
||||
if (context) {
|
||||
await context.setOffline(offline);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set extra HTTP headers
|
||||
*/
|
||||
async setExtraHeaders(headers: Record<string, string>): Promise<void> {
|
||||
const context = this.contexts[0];
|
||||
if (context) {
|
||||
await context.setExtraHTTPHeaders(headers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start tracing
|
||||
*/
|
||||
async startTracing(options: { screenshots?: boolean; snapshots?: boolean }): Promise<void> {
|
||||
const context = this.contexts[0];
|
||||
if (context) {
|
||||
await context.tracing.start({
|
||||
screenshots: options.screenshots ?? true,
|
||||
snapshots: options.snapshots ?? true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop tracing and save
|
||||
*/
|
||||
async stopTracing(path: string): Promise<void> {
|
||||
const context = this.contexts[0];
|
||||
if (context) {
|
||||
await context.tracing.stop({ path });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save storage state (cookies, localStorage, etc.)
|
||||
*/
|
||||
async saveStorageState(path: string): Promise<void> {
|
||||
const context = this.contexts[0];
|
||||
if (context) {
|
||||
await context.storageState({ path });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all pages
|
||||
*/
|
||||
|
||||
+301
@@ -307,6 +307,55 @@ function printResponse(response: Response, jsonMode: boolean): void {
|
||||
} else if (data.remaining !== undefined) {
|
||||
// Tab close
|
||||
console.log(c('green', '✓'), `Tab closed (${data.remaining} remaining)`);
|
||||
} else if (data.started !== undefined) {
|
||||
console.log(c('green', '✓'), 'Started');
|
||||
} else if (data.messages) {
|
||||
// Console messages
|
||||
const msgs = data.messages as Array<{ type: string; text: string; timestamp: number }>;
|
||||
if (msgs.length === 0) {
|
||||
console.log(c('dim', 'No console messages'));
|
||||
} else {
|
||||
msgs.forEach(msg => {
|
||||
const typeColor = msg.type === 'error' ? 'red' : msg.type === 'warning' ? 'yellow' : 'dim';
|
||||
console.log(`${c(typeColor, `[${msg.type}]`)} ${msg.text}`);
|
||||
});
|
||||
}
|
||||
} else if (data.errors) {
|
||||
// Page errors
|
||||
const errs = data.errors as Array<{ message: string; timestamp: number }>;
|
||||
if (errs.length === 0) {
|
||||
console.log(c('dim', 'No page errors'));
|
||||
} else {
|
||||
errs.forEach(err => {
|
||||
console.log(c('red', '✗'), err.message);
|
||||
});
|
||||
}
|
||||
} else if (data.pressed) {
|
||||
console.log(c('green', '✓'), `Pressed: ${data.pressed}`);
|
||||
} else if (data.tapped) {
|
||||
console.log(c('green', '✓'), 'Tapped');
|
||||
} else if (data.copied) {
|
||||
console.log(c('green', '✓'), 'Copied to clipboard');
|
||||
} else if (data.pasted) {
|
||||
console.log(c('green', '✓'), 'Pasted from clipboard');
|
||||
} else if (data.highlighted) {
|
||||
console.log(c('green', '✓'), 'Element highlighted');
|
||||
} else if (data.dispatched) {
|
||||
console.log(c('green', '✓'), `Event dispatched: ${data.dispatched}`);
|
||||
} else if (data.exposed) {
|
||||
console.log(c('green', '✓'), `Function exposed: ${data.exposed}`);
|
||||
} else if (data.added) {
|
||||
console.log(c('green', '✓'), 'Added');
|
||||
} else if (data.emulated) {
|
||||
console.log(c('green', '✓'), 'Media emulated');
|
||||
} else if (data.offline !== undefined) {
|
||||
console.log(c('green', '✓'), data.offline ? 'Offline mode enabled' : 'Online mode enabled');
|
||||
} else if (data.paused) {
|
||||
console.log(c('green', '✓'), 'Paused (Inspector open)');
|
||||
} else if (data.note) {
|
||||
console.log(c('yellow', '⚠'), data.note);
|
||||
} else if (data.requestCount !== undefined) {
|
||||
console.log(c('green', '✓'), `HAR saved (${data.requestCount} requests)`);
|
||||
} else {
|
||||
console.log(c('green', '✓'), JSON.stringify(data));
|
||||
}
|
||||
@@ -971,6 +1020,258 @@ async function main(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// === Advanced commands ===
|
||||
|
||||
case 'trace': {
|
||||
const subCmd = cleanArgs[1];
|
||||
|
||||
if (subCmd === 'start') {
|
||||
const screenshotsMode = args.includes('--screenshots');
|
||||
const snapshotsMode = args.includes('--snapshots');
|
||||
cmd = { id, action: 'trace_start', screenshots: screenshotsMode, snapshots: snapshotsMode };
|
||||
} else if (subCmd === 'stop') {
|
||||
const path = cleanArgs[2];
|
||||
if (!path) {
|
||||
console.error(c('red', 'Error:'), 'Path required for trace output');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'trace_stop', path };
|
||||
} else {
|
||||
console.error(c('red', 'Error:'), 'Usage: veb trace start|stop');
|
||||
process.exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'state': {
|
||||
const subCmd = cleanArgs[1];
|
||||
const path = cleanArgs[2];
|
||||
|
||||
if (subCmd === 'save') {
|
||||
if (!path) {
|
||||
console.error(c('red', 'Error:'), 'Path required');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'state_save', path };
|
||||
} else if (subCmd === 'load') {
|
||||
if (!path) {
|
||||
console.error(c('red', 'Error:'), 'Path required');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'state_load', path };
|
||||
} else {
|
||||
console.error(c('red', 'Error:'), 'Usage: veb state save|load <path>');
|
||||
process.exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'console': {
|
||||
const clearMode = args.includes('--clear');
|
||||
cmd = { id, action: 'console', clear: clearMode };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'errors': {
|
||||
const clearMode = args.includes('--clear');
|
||||
cmd = { id, action: 'errors', clear: clearMode };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'keyboard':
|
||||
case 'key':
|
||||
case 'press': {
|
||||
const keys = cleanArgs[1];
|
||||
if (!keys) {
|
||||
console.error(c('red', 'Error:'), 'Keys required (e.g., veb press "Control+a")');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'keyboard', keys };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'wheel':
|
||||
case 'scroll-wheel': {
|
||||
const deltaY = parseInt(cleanArgs[1], 10) || 100;
|
||||
const deltaX = parseInt(cleanArgs[2], 10) || 0;
|
||||
const selector = selectorOverride;
|
||||
cmd = { id, action: 'wheel', deltaX, deltaY, selector };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'tap': {
|
||||
const selector = cleanArgs[1];
|
||||
if (!selector) {
|
||||
console.error(c('red', 'Error:'), 'Selector required');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'tap', selector };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'clipboard': {
|
||||
const operation = cleanArgs[1] as 'copy' | 'paste' | 'read';
|
||||
if (!['copy', 'paste', 'read'].includes(operation)) {
|
||||
console.error(c('red', 'Error:'), 'Usage: veb clipboard copy|paste|read');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'clipboard', operation };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'highlight': {
|
||||
const selector = cleanArgs[1];
|
||||
if (!selector) {
|
||||
console.error(c('red', 'Error:'), 'Selector required');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'highlight', selector };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'clear-input':
|
||||
case 'clearinput': {
|
||||
const selector = cleanArgs[1];
|
||||
if (!selector) {
|
||||
console.error(c('red', 'Error:'), 'Selector required');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'clear', selector };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'selectall':
|
||||
case 'select-all': {
|
||||
const selector = cleanArgs[1];
|
||||
if (!selector) {
|
||||
console.error(c('red', 'Error:'), 'Selector required');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'selectall', selector };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'innertext': {
|
||||
const selector = cleanArgs[1];
|
||||
if (!selector) {
|
||||
console.error(c('red', 'Error:'), 'Selector required');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'innertext', selector };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'innerhtml':
|
||||
case 'inner-html': {
|
||||
const selector = cleanArgs[1];
|
||||
if (!selector) {
|
||||
console.error(c('red', 'Error:'), 'Selector required');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'innerhtml', selector };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'inputvalue':
|
||||
case 'input-value': {
|
||||
const selector = cleanArgs[1];
|
||||
if (!selector) {
|
||||
console.error(c('red', 'Error:'), 'Selector required');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'inputvalue', selector };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'setvalue':
|
||||
case 'set-value': {
|
||||
const selector = cleanArgs[1];
|
||||
const value = cleanArgs[2];
|
||||
if (!selector || !value) {
|
||||
console.error(c('red', 'Error:'), 'Selector and value required');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'setvalue', selector, value };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'dispatch': {
|
||||
const selector = cleanArgs[1];
|
||||
const event = cleanArgs[2];
|
||||
if (!selector || !event) {
|
||||
console.error(c('red', 'Error:'), 'Selector and event required');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'dispatch', selector, event };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'addscript':
|
||||
case 'add-script': {
|
||||
const urlOrContent = cleanArgs[1];
|
||||
if (!urlOrContent) {
|
||||
console.error(c('red', 'Error:'), 'URL or content required');
|
||||
process.exit(1);
|
||||
}
|
||||
if (urlOrContent.startsWith('http')) {
|
||||
cmd = { id, action: 'addscript', url: urlOrContent };
|
||||
} else {
|
||||
cmd = { id, action: 'addscript', content: urlOrContent };
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'addstyle':
|
||||
case 'add-style': {
|
||||
const urlOrContent = cleanArgs[1];
|
||||
if (!urlOrContent) {
|
||||
console.error(c('red', 'Error:'), 'URL or content required');
|
||||
process.exit(1);
|
||||
}
|
||||
if (urlOrContent.startsWith('http')) {
|
||||
cmd = { id, action: 'addstyle', url: urlOrContent };
|
||||
} else {
|
||||
cmd = { id, action: 'addstyle', content: urlOrContent };
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'media': {
|
||||
const colorScheme = args.includes('--dark') ? 'dark' : args.includes('--light') ? 'light' : undefined;
|
||||
const media = args.includes('--print') ? 'print' : args.includes('--screen') ? 'screen' : undefined;
|
||||
const reducedMotion = args.includes('--reduced-motion') ? 'reduce' : undefined;
|
||||
|
||||
cmd = { id, action: 'emulatemedia', colorScheme, media, reducedMotion };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'offline': {
|
||||
const offlineMode = cleanArgs[1] !== 'false' && cleanArgs[1] !== 'off';
|
||||
cmd = { id, action: 'offline', offline: offlineMode };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'headers': {
|
||||
const headersJson = cleanArgs[1];
|
||||
if (!headersJson) {
|
||||
console.error(c('red', 'Error:'), 'Headers JSON required');
|
||||
process.exit(1);
|
||||
}
|
||||
try {
|
||||
const headers = JSON.parse(headersJson);
|
||||
cmd = { id, action: 'headers', headers };
|
||||
} catch {
|
||||
console.error(c('red', 'Error:'), 'Invalid JSON for headers');
|
||||
process.exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'pause': {
|
||||
cmd = { id, action: 'pause' };
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
console.error(c('red', 'Error:'), `Unknown command: ${command}`);
|
||||
console.error(c('dim', 'Run veb --help for usage'));
|
||||
|
||||
+189
@@ -286,6 +286,165 @@ const boundingBoxSchema = baseCommandSchema.extend({
|
||||
selector: z.string().min(1),
|
||||
});
|
||||
|
||||
const videoStartSchema = baseCommandSchema.extend({
|
||||
action: z.literal('video_start'),
|
||||
path: z.string().min(1),
|
||||
});
|
||||
|
||||
const videoStopSchema = baseCommandSchema.extend({
|
||||
action: z.literal('video_stop'),
|
||||
});
|
||||
|
||||
const traceStartSchema = baseCommandSchema.extend({
|
||||
action: z.literal('trace_start'),
|
||||
screenshots: z.boolean().optional(),
|
||||
snapshots: z.boolean().optional(),
|
||||
});
|
||||
|
||||
const traceStopSchema = baseCommandSchema.extend({
|
||||
action: z.literal('trace_stop'),
|
||||
path: z.string().min(1),
|
||||
});
|
||||
|
||||
const harStartSchema = baseCommandSchema.extend({
|
||||
action: z.literal('har_start'),
|
||||
});
|
||||
|
||||
const harStopSchema = baseCommandSchema.extend({
|
||||
action: z.literal('har_stop'),
|
||||
path: z.string().min(1),
|
||||
});
|
||||
|
||||
const stateSaveSchema = baseCommandSchema.extend({
|
||||
action: z.literal('state_save'),
|
||||
path: z.string().min(1),
|
||||
});
|
||||
|
||||
const stateLoadSchema = baseCommandSchema.extend({
|
||||
action: z.literal('state_load'),
|
||||
path: z.string().min(1),
|
||||
});
|
||||
|
||||
const consoleSchema = baseCommandSchema.extend({
|
||||
action: z.literal('console'),
|
||||
clear: z.boolean().optional(),
|
||||
});
|
||||
|
||||
const errorsSchema = baseCommandSchema.extend({
|
||||
action: z.literal('errors'),
|
||||
clear: z.boolean().optional(),
|
||||
});
|
||||
|
||||
const keyboardSchema = baseCommandSchema.extend({
|
||||
action: z.literal('keyboard'),
|
||||
keys: z.string().min(1),
|
||||
});
|
||||
|
||||
const wheelSchema = baseCommandSchema.extend({
|
||||
action: z.literal('wheel'),
|
||||
deltaX: z.number().optional(),
|
||||
deltaY: z.number().optional(),
|
||||
selector: z.string().optional(),
|
||||
});
|
||||
|
||||
const tapSchema = baseCommandSchema.extend({
|
||||
action: z.literal('tap'),
|
||||
selector: z.string().min(1),
|
||||
});
|
||||
|
||||
const clipboardSchema = baseCommandSchema.extend({
|
||||
action: z.literal('clipboard'),
|
||||
operation: z.enum(['copy', 'paste', 'read']),
|
||||
text: z.string().optional(),
|
||||
});
|
||||
|
||||
const highlightSchema = baseCommandSchema.extend({
|
||||
action: z.literal('highlight'),
|
||||
selector: z.string().min(1),
|
||||
});
|
||||
|
||||
const clearSchema = baseCommandSchema.extend({
|
||||
action: z.literal('clear'),
|
||||
selector: z.string().min(1),
|
||||
});
|
||||
|
||||
const selectAllSchema = baseCommandSchema.extend({
|
||||
action: z.literal('selectall'),
|
||||
selector: z.string().min(1),
|
||||
});
|
||||
|
||||
const innerTextSchema = baseCommandSchema.extend({
|
||||
action: z.literal('innertext'),
|
||||
selector: z.string().min(1),
|
||||
});
|
||||
|
||||
const innerHtmlSchema = baseCommandSchema.extend({
|
||||
action: z.literal('innerhtml'),
|
||||
selector: z.string().min(1),
|
||||
});
|
||||
|
||||
const inputValueSchema = baseCommandSchema.extend({
|
||||
action: z.literal('inputvalue'),
|
||||
selector: z.string().min(1),
|
||||
});
|
||||
|
||||
const setValueSchema = baseCommandSchema.extend({
|
||||
action: z.literal('setvalue'),
|
||||
selector: z.string().min(1),
|
||||
value: z.string(),
|
||||
});
|
||||
|
||||
const dispatchSchema = baseCommandSchema.extend({
|
||||
action: z.literal('dispatch'),
|
||||
selector: z.string().min(1),
|
||||
event: z.string().min(1),
|
||||
eventInit: z.record(z.unknown()).optional(),
|
||||
});
|
||||
|
||||
const evalHandleSchema = baseCommandSchema.extend({
|
||||
action: z.literal('evalhandle'),
|
||||
script: z.string().min(1),
|
||||
});
|
||||
|
||||
const exposeSchema = baseCommandSchema.extend({
|
||||
action: z.literal('expose'),
|
||||
name: z.string().min(1),
|
||||
});
|
||||
|
||||
const addScriptSchema = baseCommandSchema.extend({
|
||||
action: z.literal('addscript'),
|
||||
content: z.string().optional(),
|
||||
url: z.string().optional(),
|
||||
});
|
||||
|
||||
const addStyleSchema = baseCommandSchema.extend({
|
||||
action: z.literal('addstyle'),
|
||||
content: z.string().optional(),
|
||||
url: z.string().optional(),
|
||||
});
|
||||
|
||||
const emulateMediaSchema = baseCommandSchema.extend({
|
||||
action: z.literal('emulatemedia'),
|
||||
media: z.enum(['screen', 'print']).nullable().optional(),
|
||||
colorScheme: z.enum(['light', 'dark', 'no-preference']).nullable().optional(),
|
||||
reducedMotion: z.enum(['reduce', 'no-preference']).nullable().optional(),
|
||||
forcedColors: z.enum(['active', 'none']).nullable().optional(),
|
||||
});
|
||||
|
||||
const offlineSchema = baseCommandSchema.extend({
|
||||
action: z.literal('offline'),
|
||||
offline: z.boolean(),
|
||||
});
|
||||
|
||||
const headersSchema = baseCommandSchema.extend({
|
||||
action: z.literal('headers'),
|
||||
headers: z.record(z.string()),
|
||||
});
|
||||
|
||||
const pauseSchema = baseCommandSchema.extend({
|
||||
action: z.literal('pause'),
|
||||
});
|
||||
|
||||
const pressSchema = baseCommandSchema.extend({
|
||||
action: z.literal('press'),
|
||||
key: z.string().min(1),
|
||||
@@ -437,6 +596,36 @@ const commandSchema = z.discriminatedUnion('action', [
|
||||
isCheckedSchema,
|
||||
countSchema,
|
||||
boundingBoxSchema,
|
||||
videoStartSchema,
|
||||
videoStopSchema,
|
||||
traceStartSchema,
|
||||
traceStopSchema,
|
||||
harStartSchema,
|
||||
harStopSchema,
|
||||
stateSaveSchema,
|
||||
stateLoadSchema,
|
||||
consoleSchema,
|
||||
errorsSchema,
|
||||
keyboardSchema,
|
||||
wheelSchema,
|
||||
tapSchema,
|
||||
clipboardSchema,
|
||||
highlightSchema,
|
||||
clearSchema,
|
||||
selectAllSchema,
|
||||
innerTextSchema,
|
||||
innerHtmlSchema,
|
||||
inputValueSchema,
|
||||
setValueSchema,
|
||||
dispatchSchema,
|
||||
evalHandleSchema,
|
||||
exposeSchema,
|
||||
addScriptSchema,
|
||||
addStyleSchema,
|
||||
emulateMediaSchema,
|
||||
offlineSchema,
|
||||
headersSchema,
|
||||
pauseSchema,
|
||||
]);
|
||||
|
||||
// Parse result type
|
||||
|
||||
+214
-1
@@ -294,6 +294,189 @@ export interface BoundingBoxCommand extends BaseCommand {
|
||||
selector: string;
|
||||
}
|
||||
|
||||
// Video recording
|
||||
export interface VideoStartCommand extends BaseCommand {
|
||||
action: 'video_start';
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface VideoStopCommand extends BaseCommand {
|
||||
action: 'video_stop';
|
||||
}
|
||||
|
||||
// Tracing
|
||||
export interface TraceStartCommand extends BaseCommand {
|
||||
action: 'trace_start';
|
||||
screenshots?: boolean;
|
||||
snapshots?: boolean;
|
||||
}
|
||||
|
||||
export interface TraceStopCommand extends BaseCommand {
|
||||
action: 'trace_stop';
|
||||
path: string;
|
||||
}
|
||||
|
||||
// HAR recording
|
||||
export interface HarStartCommand extends BaseCommand {
|
||||
action: 'har_start';
|
||||
}
|
||||
|
||||
export interface HarStopCommand extends BaseCommand {
|
||||
action: 'har_stop';
|
||||
path: string;
|
||||
}
|
||||
|
||||
// Storage state (auth persistence)
|
||||
export interface StorageStateSaveCommand extends BaseCommand {
|
||||
action: 'state_save';
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface StorageStateLoadCommand extends BaseCommand {
|
||||
action: 'state_load';
|
||||
path: string;
|
||||
}
|
||||
|
||||
// Console logs
|
||||
export interface ConsoleCommand extends BaseCommand {
|
||||
action: 'console';
|
||||
clear?: boolean;
|
||||
}
|
||||
|
||||
// Page errors
|
||||
export interface ErrorsCommand extends BaseCommand {
|
||||
action: 'errors';
|
||||
clear?: boolean;
|
||||
}
|
||||
|
||||
// Keyboard shortcuts
|
||||
export interface KeyboardCommand extends BaseCommand {
|
||||
action: 'keyboard';
|
||||
keys: string; // e.g., "Control+a", "Shift+Tab"
|
||||
}
|
||||
|
||||
// Mouse wheel
|
||||
export interface WheelCommand extends BaseCommand {
|
||||
action: 'wheel';
|
||||
deltaX?: number;
|
||||
deltaY?: number;
|
||||
selector?: string;
|
||||
}
|
||||
|
||||
// Touch events
|
||||
export interface TapCommand extends BaseCommand {
|
||||
action: 'tap';
|
||||
selector: string;
|
||||
}
|
||||
|
||||
// Clipboard
|
||||
export interface ClipboardCommand extends BaseCommand {
|
||||
action: 'clipboard';
|
||||
operation: 'copy' | 'paste' | 'read';
|
||||
text?: string;
|
||||
}
|
||||
|
||||
// Highlight element (for debugging)
|
||||
export interface HighlightCommand extends BaseCommand {
|
||||
action: 'highlight';
|
||||
selector: string;
|
||||
}
|
||||
|
||||
// Clear input
|
||||
export interface ClearCommand extends BaseCommand {
|
||||
action: 'clear';
|
||||
selector: string;
|
||||
}
|
||||
|
||||
// Select all text
|
||||
export interface SelectAllCommand extends BaseCommand {
|
||||
action: 'selectall';
|
||||
selector: string;
|
||||
}
|
||||
|
||||
// Inner text vs text content
|
||||
export interface InnerTextCommand extends BaseCommand {
|
||||
action: 'innertext';
|
||||
selector: string;
|
||||
}
|
||||
|
||||
export interface InnerHtmlCommand extends BaseCommand {
|
||||
action: 'innerhtml';
|
||||
selector: string;
|
||||
}
|
||||
|
||||
// Input value
|
||||
export interface InputValueCommand extends BaseCommand {
|
||||
action: 'inputvalue';
|
||||
selector: string;
|
||||
}
|
||||
|
||||
// Set input value directly (without events)
|
||||
export interface SetValueCommand extends BaseCommand {
|
||||
action: 'setvalue';
|
||||
selector: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
// Dispatch event
|
||||
export interface DispatchEventCommand extends BaseCommand {
|
||||
action: 'dispatch';
|
||||
selector: string;
|
||||
event: string;
|
||||
eventInit?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
// Evaluate handle (for complex JS)
|
||||
export interface EvaluateHandleCommand extends BaseCommand {
|
||||
action: 'evalhandle';
|
||||
script: string;
|
||||
}
|
||||
|
||||
// Expose function
|
||||
export interface ExposeFunctionCommand extends BaseCommand {
|
||||
action: 'expose';
|
||||
name: string;
|
||||
}
|
||||
|
||||
// Add script/style tag
|
||||
export interface AddScriptCommand extends BaseCommand {
|
||||
action: 'addscript';
|
||||
content?: string;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export interface AddStyleCommand extends BaseCommand {
|
||||
action: 'addstyle';
|
||||
content?: string;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
// Emulate media
|
||||
export interface EmulateMediaCommand extends BaseCommand {
|
||||
action: 'emulatemedia';
|
||||
media?: 'screen' | 'print' | null;
|
||||
colorScheme?: 'light' | 'dark' | 'no-preference' | null;
|
||||
reducedMotion?: 'reduce' | 'no-preference' | null;
|
||||
forcedColors?: 'active' | 'none' | null;
|
||||
}
|
||||
|
||||
// Set offline mode
|
||||
export interface OfflineCommand extends BaseCommand {
|
||||
action: 'offline';
|
||||
offline: boolean;
|
||||
}
|
||||
|
||||
// Set extra HTTP headers
|
||||
export interface HeadersCommand extends BaseCommand {
|
||||
action: 'headers';
|
||||
headers: Record<string, string>;
|
||||
}
|
||||
|
||||
// Pause execution (for debugging)
|
||||
export interface PauseCommand extends BaseCommand {
|
||||
action: 'pause';
|
||||
}
|
||||
|
||||
export interface PressCommand extends BaseCommand {
|
||||
action: 'press';
|
||||
key: string;
|
||||
@@ -441,7 +624,37 @@ export type Command =
|
||||
| IsEnabledCommand
|
||||
| IsCheckedCommand
|
||||
| CountCommand
|
||||
| BoundingBoxCommand;
|
||||
| BoundingBoxCommand
|
||||
| VideoStartCommand
|
||||
| VideoStopCommand
|
||||
| TraceStartCommand
|
||||
| TraceStopCommand
|
||||
| HarStartCommand
|
||||
| HarStopCommand
|
||||
| StorageStateSaveCommand
|
||||
| StorageStateLoadCommand
|
||||
| ConsoleCommand
|
||||
| ErrorsCommand
|
||||
| KeyboardCommand
|
||||
| WheelCommand
|
||||
| TapCommand
|
||||
| ClipboardCommand
|
||||
| HighlightCommand
|
||||
| ClearCommand
|
||||
| SelectAllCommand
|
||||
| InnerTextCommand
|
||||
| InnerHtmlCommand
|
||||
| InputValueCommand
|
||||
| SetValueCommand
|
||||
| DispatchEventCommand
|
||||
| EvaluateHandleCommand
|
||||
| ExposeFunctionCommand
|
||||
| AddScriptCommand
|
||||
| AddStyleCommand
|
||||
| EmulateMediaCommand
|
||||
| OfflineCommand
|
||||
| HeadersCommand
|
||||
| PauseCommand;
|
||||
|
||||
// Response types
|
||||
export interface SuccessResponse<T = unknown> {
|
||||
|
||||
Reference in New Issue
Block a user