UNPKG

browser-debugger-cli

Version:

DevTools telemetry in your terminal. For humans and agents. Direct WebSocket to Chrome's debugging port.

79 lines 7.53 kB
/** * React-compatible event handling for form interactions. * * React uses synthetic events and doesn't detect direct DOM manipulation. * This module provides JavaScript snippets that can be injected via Runtime.evaluate * to properly trigger React's event system. */ /** * JavaScript function to fill an input element in a React-compatible way. * * This approach: * 1. Uses native property setters to bypass React's value tracking * 2. Dispatches input/change events that React listens for * 3. Properly handles focus/blur for form validation * * @remarks * Works with React, Vue, Angular, and vanilla JS applications. */ export declare const REACT_FILL_SCRIPT = "\n(function(selector, value, options) {\n const allMatches = document.querySelectorAll(selector);\n \n if (allMatches.length === 0) {\n return { \n success: false, \n error: 'Element not found',\n selector: selector\n };\n }\n \n let el;\n const index = options.index;\n \n // If index is provided, use it directly (0-based)\n if (typeof index === 'number' && index >= 0) {\n if (index >= allMatches.length) {\n return {\n success: false,\n error: 'Index out of range',\n selector: selector,\n matchCount: allMatches.length,\n requestedIndex: index,\n suggestion: 'Use --index between 0 and ' + (allMatches.length - 1)\n };\n }\n el = allMatches[index];\n } else {\n el = allMatches[0];\n }\n \n const tagName = el.tagName.toLowerCase();\n const inputType = el.type?.toLowerCase();\n \n const isFillable = (\n tagName === 'input' || \n tagName === 'textarea' || \n tagName === 'select' ||\n el.isContentEditable\n );\n \n if (!isFillable) {\n return {\n success: false,\n error: 'Element is not fillable',\n elementType: tagName,\n suggestion: 'Only input, textarea, select, and contenteditable elements can be filled'\n };\n }\n \n el.focus();\n \n if (tagName === 'select') {\n el.value = value;\n el.dispatchEvent(new Event('change', { bubbles: true }));\n } else if (inputType === 'checkbox' || inputType === 'radio') {\n const shouldCheck = value === 'true' || value === true;\n el.checked = shouldCheck;\n el.dispatchEvent(new Event('change', { bubbles: true }));\n } else if (inputType === 'file') {\n return {\n success: false,\n error: 'File inputs require CDP DOM.setFileInputFiles method',\n suggestion: 'Use: bdg cdp DOM.setFileInputFiles --params {\\\"files\\\":[\\\"path\\\"]}'\n };\n } else if (el.isContentEditable) {\n el.textContent = value;\n el.dispatchEvent(new Event('input', { bubbles: true }));\n } else {\n const nativeInputValueSetter = Object.getOwnPropertyDescriptor(\n window.HTMLInputElement.prototype,\n 'value'\n )?.set;\n \n const nativeTextAreaValueSetter = Object.getOwnPropertyDescriptor(\n window.HTMLTextAreaElement.prototype,\n 'value'\n )?.set;\n \n const setter = tagName === 'textarea' \n ? nativeTextAreaValueSetter \n : nativeInputValueSetter;\n \n if (setter) {\n setter.call(el, value);\n } else {\n el.value = value;\n }\n \n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n }\n \n if (options.blur !== false) {\n el.blur();\n el.dispatchEvent(new FocusEvent('focusout', { bubbles: true }));\n }\n \n return {\n success: true,\n selector: selector,\n value: el.value || el.textContent,\n elementType: tagName,\n inputType: inputType || null,\n checked: el.checked || undefined\n };\n})\n"; /** * JavaScript function to click an element. * * @remarks * Handles both direct selector matching and indexed selection. * When index is provided, selects the nth matching element (0-based). * When selector matches multiple elements without index, prioritizes visible ones. */ export declare const CLICK_ELEMENT_SCRIPT = "\n(function(selector, index) {\n const allMatches = document.querySelectorAll(selector);\n \n if (allMatches.length === 0) {\n return {\n success: false,\n error: 'Element not found',\n selector: selector\n };\n }\n \n let el;\n \n // If index is provided, use it directly (0-based)\n if (typeof index === 'number' && index >= 0) {\n if (index >= allMatches.length) {\n return {\n success: false,\n error: 'Index out of range',\n selector: selector,\n matchCount: allMatches.length,\n requestedIndex: index,\n suggestion: 'Use --index between 0 and ' + (allMatches.length - 1)\n };\n }\n el = allMatches[index];\n } else if (allMatches.length === 1) {\n // Single match - use it directly\n el = allMatches[0];\n } else {\n // Multiple matches without index - find first visible one\n el = allMatches[0];\n for (const candidate of allMatches) {\n const style = window.getComputedStyle(candidate);\n const rect = candidate.getBoundingClientRect();\n \n const hasSize = rect.width > 0 && rect.height > 0;\n const isDisplayed = style.display !== 'none' && style.visibility !== 'hidden';\n const isOpaque = parseFloat(style.opacity) > 0;\n const isPositioned = candidate.offsetParent !== null || style.position === 'fixed';\n \n const isVisible = hasSize && isDisplayed && isOpaque && isPositioned;\n \n if (isVisible) {\n el = candidate;\n break;\n }\n }\n }\n \n const tagName = el.tagName.toLowerCase();\n const isClickable = (\n tagName === 'button' ||\n tagName === 'a' ||\n tagName === 'input' ||\n el.onclick !== null ||\n el.getAttribute('role') === 'button' ||\n window.getComputedStyle(el).cursor === 'pointer'\n );\n \n if (!isClickable) {\n console.warn('Warning: Element may not be clickable:', el);\n }\n \n el.scrollIntoView({ behavior: 'auto', block: 'center' });\n \n el.click();\n \n return {\n success: true,\n selector: selector,\n elementType: tagName,\n clickable: isClickable,\n matchCount: allMatches.length,\n selectedIndex: typeof index === 'number' ? index : undefined\n };\n})\n"; /** * Options for filling an element. */ export interface FillOptions { /** Whether to blur the element after filling (default: true) */ blur?: boolean; /** Index to use if selector matches multiple elements (0-based) */ index?: number; } /** * Result of filling an element. */ export interface FillResult { success: boolean; error?: string; selector?: string; value?: string; elementType?: string; inputType?: string | null; checked?: boolean; suggestion?: string; } /** * Result of clicking an element. */ export interface ClickResult { success: boolean; error?: string; selector?: string; elementType?: string; clickable?: boolean; matchCount?: number; selectedIndex?: number; requestedIndex?: number; suggestion?: string; } /** * Type guard for FillResult. * * @param value - Value to check * @returns True if value is a valid FillResult */ export declare function isFillResult(value: unknown): value is FillResult; /** * Type guard for ClickResult. * * @param value - Value to check * @returns True if value is a valid ClickResult */ export declare function isClickResult(value: unknown): value is ClickResult; //# sourceMappingURL=reactEventHelpers.d.ts.map