browser-debugger-cli 0.6.4 → 0.6.6
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/dist/commands/__tests__/machineReadableHelp.contract.test.d.ts +12 -0
- package/dist/commands/__tests__/machineReadableHelp.contract.test.d.ts.map +1 -0
- package/dist/commands/__tests__/machineReadableHelp.contract.test.js +102 -0
- package/dist/commands/__tests__/machineReadableHelp.contract.test.js.map +1 -0
- package/dist/commands/cdp.js +8 -4
- package/dist/commands/cdp.js.map +1 -1
- package/dist/commands/console.d.ts.map +1 -1
- package/dist/commands/console.js +22 -2
- package/dist/commands/console.js.map +1 -1
- package/dist/commands/dom/a11y.d.ts.map +1 -1
- package/dist/commands/dom/a11y.js +83 -19
- package/dist/commands/dom/a11y.js.map +1 -1
- package/dist/commands/dom/evalHelpers.d.ts +1 -1
- package/dist/commands/dom/evalHelpers.d.ts.map +1 -1
- package/dist/commands/dom/evalHelpers.js +3 -2
- package/dist/commands/dom/evalHelpers.js.map +1 -1
- package/dist/commands/dom/formFillHelpers.d.ts +68 -2
- package/dist/commands/dom/formFillHelpers.d.ts.map +1 -1
- package/dist/commands/dom/formFillHelpers.js +240 -66
- package/dist/commands/dom/formFillHelpers.js.map +1 -1
- package/dist/commands/dom/formInteraction.d.ts.map +1 -1
- package/dist/commands/dom/formInteraction.js +128 -15
- package/dist/commands/dom/formInteraction.js.map +1 -1
- package/dist/commands/dom/helpers.d.ts +61 -2
- package/dist/commands/dom/helpers.d.ts.map +1 -1
- package/dist/commands/dom/helpers.js +103 -0
- package/dist/commands/dom/helpers.js.map +1 -1
- package/dist/commands/dom/index.d.ts.map +1 -1
- package/dist/commands/dom/index.js +135 -16
- package/dist/commands/dom/index.js.map +1 -1
- package/dist/commands/dom/keyMapping.d.ts +62 -0
- package/dist/commands/dom/keyMapping.d.ts.map +1 -0
- package/dist/commands/dom/keyMapping.js +118 -0
- package/dist/commands/dom/keyMapping.js.map +1 -0
- package/dist/commands/dom/reactEventHelpers.d.ts +10 -5
- package/dist/commands/dom/reactEventHelpers.d.ts.map +1 -1
- package/dist/commands/dom/reactEventHelpers.js +55 -13
- package/dist/commands/dom/reactEventHelpers.js.map +1 -1
- package/dist/commands/machineReadableHelp.d.ts +48 -0
- package/dist/commands/machineReadableHelp.d.ts.map +1 -1
- package/dist/commands/machineReadableHelp.js +80 -0
- package/dist/commands/machineReadableHelp.js.map +1 -1
- package/dist/commands/network.d.ts.map +1 -1
- package/dist/commands/network.js +28 -2
- package/dist/commands/network.js.map +1 -1
- package/dist/commands/peek.d.ts.map +1 -1
- package/dist/commands/peek.js +16 -7
- package/dist/commands/peek.js.map +1 -1
- package/dist/commands/shared/CommandRunner.d.ts +2 -0
- package/dist/commands/shared/CommandRunner.d.ts.map +1 -1
- package/dist/commands/shared/CommandRunner.js +3 -0
- package/dist/commands/shared/CommandRunner.js.map +1 -1
- package/dist/commands/shared/validation.d.ts +2 -0
- package/dist/commands/shared/validation.d.ts.map +1 -1
- package/dist/commands/shared/validation.js +5 -2
- package/dist/commands/shared/validation.js.map +1 -1
- package/dist/commands/stop.d.ts.map +1 -1
- package/dist/commands/stop.js +2 -1
- package/dist/commands/stop.js.map +1 -1
- package/dist/commands/tail.d.ts.map +1 -1
- package/dist/commands/tail.js +6 -0
- package/dist/commands/tail.js.map +1 -1
- package/dist/connection/cdp.js +9 -9
- package/dist/constants.d.ts +5 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +9 -3
- package/dist/constants.js.map +1 -1
- package/dist/daemon/patternDefinitions.d.ts +47 -0
- package/dist/daemon/patternDefinitions.d.ts.map +1 -0
- package/dist/daemon/patternDefinitions.js +71 -0
- package/dist/daemon/patternDefinitions.js.map +1 -0
- package/dist/daemon/patternDetector.d.ts +60 -0
- package/dist/daemon/patternDetector.d.ts.map +1 -0
- package/dist/daemon/patternDetector.js +74 -0
- package/dist/daemon/patternDetector.js.map +1 -0
- package/dist/daemon/worker/__tests__/PatternDetector.unit.test.d.ts +11 -0
- package/dist/daemon/worker/__tests__/PatternDetector.unit.test.d.ts.map +1 -0
- package/dist/daemon/worker/__tests__/PatternDetector.unit.test.js +136 -0
- package/dist/daemon/worker/__tests__/PatternDetector.unit.test.js.map +1 -0
- package/dist/daemon/worker/commandRegistry.d.ts.map +1 -1
- package/dist/daemon/worker/commandRegistry.js +13 -2
- package/dist/daemon/worker/commandRegistry.js.map +1 -1
- package/dist/ipc/__tests__/jsonl.unit.test.js +5 -5
- package/dist/ipc/__tests__/jsonl.unit.test.js.map +1 -1
- package/dist/ipc/protocol/commands.d.ts +2 -0
- package/dist/ipc/protocol/commands.d.ts.map +1 -1
- package/dist/ipc/protocol/commands.js.map +1 -1
- package/dist/session/cleanup.d.ts.map +1 -1
- package/dist/session/cleanup.js +2 -0
- package/dist/session/cleanup.js.map +1 -1
- package/dist/session/queryCache.d.ts +35 -0
- package/dist/session/queryCache.d.ts.map +1 -0
- package/dist/session/queryCache.js +83 -0
- package/dist/session/queryCache.js.map +1 -0
- package/dist/telemetry/a11y.d.ts +8 -5
- package/dist/telemetry/a11y.d.ts.map +1 -1
- package/dist/telemetry/a11y.js +34 -19
- package/dist/telemetry/a11y.js.map +1 -1
- package/dist/types/dom.d.ts +9 -0
- package/dist/types/dom.d.ts.map +1 -1
- package/dist/ui/errors/CommandError.d.ts +5 -1
- package/dist/ui/errors/CommandError.d.ts.map +1 -1
- package/dist/ui/errors/CommandError.js.map +1 -1
- package/dist/ui/formatters/__tests__/preview.unit.test.d.ts +13 -0
- package/dist/ui/formatters/__tests__/preview.unit.test.d.ts.map +1 -0
- package/dist/ui/formatters/__tests__/preview.unit.test.js +180 -0
- package/dist/ui/formatters/__tests__/preview.unit.test.js.map +1 -0
- package/dist/ui/formatters/a11y.d.ts +19 -0
- package/dist/ui/formatters/a11y.d.ts.map +1 -1
- package/dist/ui/formatters/a11y.js +60 -0
- package/dist/ui/formatters/a11y.js.map +1 -1
- package/dist/ui/formatters/dom.d.ts.map +1 -1
- package/dist/ui/formatters/dom.js +5 -2
- package/dist/ui/formatters/dom.js.map +1 -1
- package/dist/ui/formatters/helpFormatters.d.ts +41 -0
- package/dist/ui/formatters/helpFormatters.d.ts.map +1 -0
- package/dist/ui/formatters/helpFormatters.js +80 -0
- package/dist/ui/formatters/helpFormatters.js.map +1 -0
- package/dist/ui/formatters/preview.d.ts +4 -0
- package/dist/ui/formatters/preview.d.ts.map +1 -1
- package/dist/ui/formatters/preview.js +91 -23
- package/dist/ui/formatters/preview.js.map +1 -1
- package/dist/ui/formatters/sessionFormatters.d.ts +58 -0
- package/dist/ui/formatters/sessionFormatters.d.ts.map +1 -0
- package/dist/ui/formatters/sessionFormatters.js +118 -0
- package/dist/ui/formatters/sessionFormatters.js.map +1 -0
- package/dist/ui/messages/commands.d.ts +9 -0
- package/dist/ui/messages/commands.d.ts.map +1 -1
- package/dist/ui/messages/commands.js +11 -1
- package/dist/ui/messages/commands.js.map +1 -1
- package/dist/ui/messages/errors.d.ts +53 -0
- package/dist/ui/messages/errors.d.ts.map +1 -1
- package/dist/ui/messages/errors.js +59 -0
- package/dist/ui/messages/errors.js.map +1 -1
- package/dist/ui/messages/hints.d.ts +33 -0
- package/dist/ui/messages/hints.d.ts.map +1 -0
- package/dist/ui/messages/hints.js +37 -0
- package/dist/ui/messages/hints.js.map +1 -0
- package/dist/ui/messages/session.d.ts +11 -1
- package/dist/ui/messages/session.d.ts.map +1 -1
- package/dist/ui/messages/session.js +14 -21
- package/dist/ui/messages/session.js.map +1 -1
- package/dist/utils/decisionTrees.d.ts +54 -0
- package/dist/utils/decisionTrees.d.ts.map +1 -0
- package/dist/utils/decisionTrees.js +176 -0
- package/dist/utils/decisionTrees.js.map +1 -0
- package/dist/utils/taskMappings.d.ts +47 -0
- package/dist/utils/taskMappings.d.ts.map +1 -0
- package/dist/utils/taskMappings.js +123 -0
- package/dist/utils/taskMappings.js.map +1 -0
- package/dist/utils/url.d.ts.map +1 -1
- package/dist/utils/url.js +15 -2
- package/dist/utils/url.js.map +1 -1
- package/package.json +3 -3
|
@@ -2,15 +2,50 @@
|
|
|
2
2
|
* Form interaction helpers for filling inputs, clicking buttons, etc.
|
|
3
3
|
*/
|
|
4
4
|
import { CommandError } from '../../ui/errors/index.js';
|
|
5
|
+
import { createLogger } from '../../ui/logging/index.js';
|
|
5
6
|
import { EXIT_CODES } from '../../utils/exitCodes.js';
|
|
6
|
-
import {
|
|
7
|
+
import { getKeyDefinition, parseModifiers } from './keyMapping.js';
|
|
8
|
+
import { REACT_FILL_SCRIPT, CLICK_ELEMENT_SCRIPT, } from './reactEventHelpers.js';
|
|
9
|
+
const log = createLogger('dom');
|
|
10
|
+
/** Network idle threshold for post-action stability (ms) */
|
|
11
|
+
const ACTION_NETWORK_IDLE_MS = 150;
|
|
12
|
+
/** Maximum time to wait for post-action stability (ms) */
|
|
13
|
+
const ACTION_STABILITY_TIMEOUT_MS = 2000;
|
|
14
|
+
/** Check interval for stability polling (ms) */
|
|
15
|
+
const STABILITY_CHECK_INTERVAL_MS = 50;
|
|
16
|
+
/**
|
|
17
|
+
* Format exception details into a user-friendly error message with troubleshooting hints.
|
|
18
|
+
*
|
|
19
|
+
* @param exceptionDetails - CDP exception details
|
|
20
|
+
* @param selector - CSS selector that was used
|
|
21
|
+
* @param operationType - Type of operation (fill or click) for tailored hints
|
|
22
|
+
* @returns Formatted error message with context
|
|
23
|
+
*/
|
|
24
|
+
function formatScriptExecutionError(exceptionDetails, selector, operationType = 'fill') {
|
|
25
|
+
const errorText = exceptionDetails.text || 'Unknown error';
|
|
26
|
+
const location = exceptionDetails.lineNumber !== undefined && exceptionDetails.columnNumber !== undefined
|
|
27
|
+
? ` at line ${exceptionDetails.lineNumber + 1}, column ${exceptionDetails.columnNumber + 1}`
|
|
28
|
+
: '';
|
|
29
|
+
const troubleshootingSteps = operationType === 'fill'
|
|
30
|
+
? [
|
|
31
|
+
`1. Verify element exists: bdg dom query "${selector}"`,
|
|
32
|
+
'2. Check element is visible and not disabled',
|
|
33
|
+
`3. Try direct eval: bdg dom eval "document.querySelector('${escapeSelectorForJS(selector)}').value = 'your-value'"`,
|
|
34
|
+
]
|
|
35
|
+
: [
|
|
36
|
+
`1. Verify element exists: bdg dom query "${selector}"`,
|
|
37
|
+
'2. Check element is visible and clickable',
|
|
38
|
+
`3. Try direct eval: bdg dom eval "document.querySelector('${escapeSelectorForJS(selector)}').click()"`,
|
|
39
|
+
];
|
|
40
|
+
return `Script execution failed: ${errorText}${location}\n\nTroubleshooting:\n ${troubleshootingSteps.join('\n ')}`;
|
|
41
|
+
}
|
|
7
42
|
/**
|
|
8
43
|
* Fill a form element with a value in a React-compatible way.
|
|
9
44
|
*
|
|
10
45
|
* @param cdp - CDP connection
|
|
11
46
|
* @param selector - CSS selector for the element
|
|
12
47
|
* @param value - Value to fill
|
|
13
|
-
* @param options - Fill options
|
|
48
|
+
* @param options - Fill options including optional index for multiple matches
|
|
14
49
|
* @returns Promise resolving to fill result
|
|
15
50
|
*
|
|
16
51
|
* @throws CommandError When element operations fail
|
|
@@ -24,18 +59,11 @@ import { REACT_FILL_SCRIPT, CLICK_ELEMENT_SCRIPT, GET_ELEMENT_BY_INDEX_SCRIPT, }
|
|
|
24
59
|
* ```
|
|
25
60
|
*/
|
|
26
61
|
export async function fillElement(cdp, selector, value, options = {}) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
if (!indexResult.uniqueSelector) {
|
|
34
|
-
throw new CommandError('Failed to resolve unique selector for element index', { note: 'Element index resolution returned no unique selector' }, EXIT_CODES.SOFTWARE_ERROR);
|
|
35
|
-
}
|
|
36
|
-
targetSelector = indexResult.uniqueSelector;
|
|
37
|
-
}
|
|
38
|
-
const expression = `(${REACT_FILL_SCRIPT})('${escapeSelectorForJS(targetSelector)}', '${escapeValueForJS(value)}', ${JSON.stringify({ blur: options.blur ?? true })})`;
|
|
62
|
+
const scriptOptions = {
|
|
63
|
+
blur: options.blur ?? true,
|
|
64
|
+
index: options.index,
|
|
65
|
+
};
|
|
66
|
+
const expression = `(${REACT_FILL_SCRIPT})('${escapeSelectorForJS(selector)}', '${escapeValueForJS(value)}', ${JSON.stringify(scriptOptions)})`;
|
|
39
67
|
try {
|
|
40
68
|
const response = await cdp.send('Runtime.evaluate', {
|
|
41
69
|
expression,
|
|
@@ -44,7 +72,8 @@ export async function fillElement(cdp, selector, value, options = {}) {
|
|
|
44
72
|
});
|
|
45
73
|
const cdpResponse = response;
|
|
46
74
|
if (cdpResponse.exceptionDetails) {
|
|
47
|
-
|
|
75
|
+
const errorMessage = formatScriptExecutionError(cdpResponse.exceptionDetails, selector, 'fill');
|
|
76
|
+
throw new CommandError(errorMessage, {}, EXIT_CODES.SOFTWARE_ERROR);
|
|
48
77
|
}
|
|
49
78
|
if (cdpResponse.result?.value) {
|
|
50
79
|
return cdpResponse.result.value;
|
|
@@ -64,7 +93,7 @@ export async function fillElement(cdp, selector, value, options = {}) {
|
|
|
64
93
|
*
|
|
65
94
|
* @param cdp - CDP connection
|
|
66
95
|
* @param selector - CSS selector for the element
|
|
67
|
-
* @param options - Click options
|
|
96
|
+
* @param options - Click options including optional index for multiple matches
|
|
68
97
|
* @returns Promise resolving to click result
|
|
69
98
|
*
|
|
70
99
|
* @throws CommandError When element operations fail
|
|
@@ -78,18 +107,8 @@ export async function fillElement(cdp, selector, value, options = {}) {
|
|
|
78
107
|
* ```
|
|
79
108
|
*/
|
|
80
109
|
export async function clickElement(cdp, selector, options = {}) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const indexResult = await getElementByIndex(cdp, selector, options.index);
|
|
84
|
-
if (!indexResult.success) {
|
|
85
|
-
return indexResult;
|
|
86
|
-
}
|
|
87
|
-
if (!indexResult.uniqueSelector) {
|
|
88
|
-
throw new CommandError('Failed to resolve unique selector for element index', { note: 'Element index resolution returned no unique selector' }, EXIT_CODES.SOFTWARE_ERROR);
|
|
89
|
-
}
|
|
90
|
-
targetSelector = indexResult.uniqueSelector;
|
|
91
|
-
}
|
|
92
|
-
const expression = `(${CLICK_ELEMENT_SCRIPT})('${escapeSelectorForJS(targetSelector)}')`;
|
|
110
|
+
const indexArg = options.index ?? 'null';
|
|
111
|
+
const expression = `(${CLICK_ELEMENT_SCRIPT})('${escapeSelectorForJS(selector)}', ${indexArg})`;
|
|
93
112
|
try {
|
|
94
113
|
const response = await cdp.send('Runtime.evaluate', {
|
|
95
114
|
expression,
|
|
@@ -98,7 +117,8 @@ export async function clickElement(cdp, selector, options = {}) {
|
|
|
98
117
|
});
|
|
99
118
|
const cdpResponse = response;
|
|
100
119
|
if (cdpResponse.exceptionDetails) {
|
|
101
|
-
|
|
120
|
+
const errorMessage = formatScriptExecutionError(cdpResponse.exceptionDetails, selector, 'click');
|
|
121
|
+
throw new CommandError(errorMessage, {}, EXIT_CODES.SOFTWARE_ERROR);
|
|
102
122
|
}
|
|
103
123
|
if (cdpResponse.result?.value) {
|
|
104
124
|
return cdpResponse.result.value;
|
|
@@ -114,63 +134,217 @@ export async function clickElement(cdp, selector, options = {}) {
|
|
|
114
134
|
}
|
|
115
135
|
}
|
|
116
136
|
/**
|
|
117
|
-
*
|
|
137
|
+
* Escape CSS selector for safe inclusion in JavaScript single-quoted string.
|
|
138
|
+
* Uses JSON.stringify for special characters, then escapes single quotes
|
|
139
|
+
* since the expression wrapper uses single quotes.
|
|
118
140
|
*
|
|
119
|
-
* @param
|
|
120
|
-
* @
|
|
121
|
-
* @param index - Element index (1-based)
|
|
122
|
-
* @returns Promise resolving to element info
|
|
141
|
+
* @param selector - CSS selector to escape
|
|
142
|
+
* @returns Escaped selector safe for single-quoted JS string
|
|
123
143
|
*
|
|
124
|
-
* @
|
|
144
|
+
* @internal
|
|
145
|
+
*/
|
|
146
|
+
function escapeSelectorForJS(selector) {
|
|
147
|
+
return JSON.stringify(selector).slice(1, -1).replace(/'/g, "\\'");
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Escape value for safe inclusion in JavaScript single-quoted string.
|
|
151
|
+
* Uses JSON.stringify for special characters, then escapes single quotes
|
|
152
|
+
* since the expression wrapper uses single quotes.
|
|
153
|
+
*
|
|
154
|
+
* @param value - Value to escape
|
|
155
|
+
* @returns Escaped value safe for single-quoted JS string
|
|
125
156
|
*
|
|
126
157
|
* @internal
|
|
127
158
|
*/
|
|
128
|
-
|
|
129
|
-
|
|
159
|
+
function escapeValueForJS(value) {
|
|
160
|
+
return JSON.stringify(value).slice(1, -1).replace(/'/g, "\\'");
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Wait for page to stabilize after an action (click, fill, etc.).
|
|
164
|
+
*
|
|
165
|
+
* This is a lightweight stability check designed for post-action waiting:
|
|
166
|
+
* - Waits for network to be idle for 150ms
|
|
167
|
+
* - Times out after 2s to avoid hanging
|
|
168
|
+
* - Does not block on slow background requests
|
|
169
|
+
*
|
|
170
|
+
* @param cdp - CDP connection
|
|
171
|
+
* @returns Promise that resolves when stable or timeout reached
|
|
172
|
+
*
|
|
173
|
+
* @remarks
|
|
174
|
+
* This is intentionally less strict than page readiness on initial load.
|
|
175
|
+
* It's designed to catch immediate reactions to user actions (AJAX, re-renders)
|
|
176
|
+
* without waiting for unrelated background activity.
|
|
177
|
+
*/
|
|
178
|
+
export async function waitForActionStability(cdp) {
|
|
179
|
+
const deadline = Date.now() + ACTION_STABILITY_TIMEOUT_MS;
|
|
180
|
+
let activeRequests = 0;
|
|
181
|
+
let lastActivity = Date.now();
|
|
182
|
+
const requestHandler = () => {
|
|
183
|
+
activeRequests++;
|
|
184
|
+
lastActivity = Date.now();
|
|
185
|
+
};
|
|
186
|
+
const finishHandler = () => {
|
|
187
|
+
activeRequests--;
|
|
188
|
+
if (activeRequests === 0) {
|
|
189
|
+
lastActivity = Date.now();
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
await cdp.send('Network.enable');
|
|
193
|
+
const cleanupRequest = cdp.on('Network.requestWillBeSent', requestHandler);
|
|
194
|
+
const cleanupFinished = cdp.on('Network.loadingFinished', finishHandler);
|
|
195
|
+
const cleanupFailed = cdp.on('Network.loadingFailed', finishHandler);
|
|
130
196
|
try {
|
|
131
|
-
|
|
132
|
-
|
|
197
|
+
while (Date.now() < deadline) {
|
|
198
|
+
if (activeRequests === 0) {
|
|
199
|
+
const idleTime = Date.now() - lastActivity;
|
|
200
|
+
if (idleTime >= ACTION_NETWORK_IDLE_MS) {
|
|
201
|
+
log.debug(`Network stable after ${idleTime}ms idle`);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
await delay(STABILITY_CHECK_INTERVAL_MS);
|
|
206
|
+
}
|
|
207
|
+
log.debug('Stability timeout reached, proceeding');
|
|
208
|
+
}
|
|
209
|
+
finally {
|
|
210
|
+
cleanupRequest();
|
|
211
|
+
cleanupFinished();
|
|
212
|
+
cleanupFailed();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Delay utility.
|
|
217
|
+
*
|
|
218
|
+
* @param ms - Milliseconds to delay
|
|
219
|
+
*/
|
|
220
|
+
function delay(ms) {
|
|
221
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Script to focus an element by selector and optional index.
|
|
225
|
+
*
|
|
226
|
+
* @returns Object with success status and element info
|
|
227
|
+
*/
|
|
228
|
+
const FOCUS_ELEMENT_SCRIPT = `
|
|
229
|
+
(function(selector, index) {
|
|
230
|
+
const allMatches = document.querySelectorAll(selector);
|
|
231
|
+
if (allMatches.length === 0) {
|
|
232
|
+
return { success: false, error: 'No elements found matching selector: ' + selector };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
let el;
|
|
236
|
+
if (typeof index === 'number' && index > 0) {
|
|
237
|
+
if (index > allMatches.length) {
|
|
238
|
+
return {
|
|
239
|
+
success: false,
|
|
240
|
+
error: 'Index ' + index + ' out of range (found ' + allMatches.length + ' elements)'
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
el = allMatches[index - 1];
|
|
244
|
+
} else {
|
|
245
|
+
el = allMatches[0];
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
el.focus();
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
success: true,
|
|
252
|
+
selector: selector,
|
|
253
|
+
elementType: el.tagName.toLowerCase(),
|
|
254
|
+
focused: document.activeElement === el
|
|
255
|
+
};
|
|
256
|
+
})`;
|
|
257
|
+
/**
|
|
258
|
+
* Press a key on an element.
|
|
259
|
+
*
|
|
260
|
+
* Focuses the element first, then dispatches keyDown and keyUp events via CDP.
|
|
261
|
+
*
|
|
262
|
+
* @param cdp - CDP connection
|
|
263
|
+
* @param selector - CSS selector for the element
|
|
264
|
+
* @param keyName - Key name (e.g., "Enter", "Tab", "a")
|
|
265
|
+
* @param options - Press key options
|
|
266
|
+
* @returns Promise resolving to press key result
|
|
267
|
+
*
|
|
268
|
+
* @throws CommandError When element or key operations fail
|
|
269
|
+
*
|
|
270
|
+
* @example
|
|
271
|
+
* ```typescript
|
|
272
|
+
* // Press Enter on a todo input
|
|
273
|
+
* const result = await pressKeyElement(cdp, '.new-todo', 'Enter');
|
|
274
|
+
*
|
|
275
|
+
* // Press Tab 3 times with Shift held
|
|
276
|
+
* const result = await pressKeyElement(cdp, 'input', 'Tab', {
|
|
277
|
+
* times: 3,
|
|
278
|
+
* modifiers: 'shift'
|
|
279
|
+
* });
|
|
280
|
+
* ```
|
|
281
|
+
*/
|
|
282
|
+
export async function pressKeyElement(cdp, selector, keyName, options = {}) {
|
|
283
|
+
const keyDef = getKeyDefinition(keyName);
|
|
284
|
+
if (!keyDef) {
|
|
285
|
+
return {
|
|
286
|
+
success: false,
|
|
287
|
+
error: `Unknown key: "${keyName}". Supported keys: Enter, Tab, Escape, Space, Backspace, Delete, ArrowUp/Down/Left/Right, Home, End, PageUp, PageDown, F1-F12, a-z, 0-9`,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
const times = options.times ?? 1;
|
|
291
|
+
const modifierFlags = parseModifiers(options.modifiers);
|
|
292
|
+
const indexArg = options.index ?? 'null';
|
|
293
|
+
const focusExpression = `(${FOCUS_ELEMENT_SCRIPT})('${escapeSelectorForJS(selector)}', ${indexArg})`;
|
|
294
|
+
try {
|
|
295
|
+
const focusResponse = await cdp.send('Runtime.evaluate', {
|
|
296
|
+
expression: focusExpression,
|
|
133
297
|
returnByValue: true,
|
|
134
298
|
});
|
|
135
|
-
const
|
|
136
|
-
if (
|
|
137
|
-
throw new CommandError(
|
|
299
|
+
const focusCdpResponse = focusResponse;
|
|
300
|
+
if (focusCdpResponse.exceptionDetails) {
|
|
301
|
+
throw new CommandError(`Failed to focus element: ${focusCdpResponse.exceptionDetails.text}`, { note: `Selector: ${selector}` }, EXIT_CODES.SOFTWARE_ERROR);
|
|
138
302
|
}
|
|
139
|
-
|
|
140
|
-
|
|
303
|
+
const focusResult = focusCdpResponse.result?.value;
|
|
304
|
+
if (!focusResult?.success) {
|
|
305
|
+
return {
|
|
306
|
+
success: false,
|
|
307
|
+
error: focusResult?.error ?? 'Failed to focus element',
|
|
308
|
+
selector,
|
|
309
|
+
};
|
|
141
310
|
}
|
|
142
|
-
|
|
311
|
+
for (let i = 0; i < times; i++) {
|
|
312
|
+
await dispatchKeyEvent(cdp, 'keyDown', keyDef, modifierFlags);
|
|
313
|
+
await dispatchKeyEvent(cdp, 'keyUp', keyDef, modifierFlags);
|
|
314
|
+
}
|
|
315
|
+
return {
|
|
316
|
+
success: true,
|
|
317
|
+
selector,
|
|
318
|
+
key: keyName,
|
|
319
|
+
times,
|
|
320
|
+
modifiers: modifierFlags,
|
|
321
|
+
elementType: focusResult.elementType,
|
|
322
|
+
};
|
|
143
323
|
}
|
|
144
324
|
catch (error) {
|
|
145
325
|
if (error instanceof CommandError) {
|
|
146
326
|
throw error;
|
|
147
327
|
}
|
|
148
328
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
149
|
-
throw new CommandError('Failed to
|
|
329
|
+
throw new CommandError('Failed to press key', { note: errorMessage }, EXIT_CODES.SOFTWARE_ERROR);
|
|
150
330
|
}
|
|
151
331
|
}
|
|
152
332
|
/**
|
|
153
|
-
*
|
|
154
|
-
* Uses JSON.stringify to handle all special characters safely.
|
|
155
|
-
*
|
|
156
|
-
* @param selector - CSS selector to escape
|
|
157
|
-
* @returns Escaped selector (without surrounding quotes)
|
|
333
|
+
* Dispatch a single key event via CDP Input.dispatchKeyEvent.
|
|
158
334
|
*
|
|
159
|
-
* @
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Escape value for safe inclusion in JavaScript string.
|
|
166
|
-
* Uses JSON.stringify to handle all special characters safely.
|
|
167
|
-
*
|
|
168
|
-
* @param value - Value to escape
|
|
169
|
-
* @returns Escaped value (without surrounding quotes)
|
|
170
|
-
*
|
|
171
|
-
* @internal
|
|
335
|
+
* @param cdp - CDP connection
|
|
336
|
+
* @param type - Event type (keyDown or keyUp)
|
|
337
|
+
* @param keyDef - Key definition with code, key, and keyCode
|
|
338
|
+
* @param modifiers - Modifier bit flags
|
|
172
339
|
*/
|
|
173
|
-
function
|
|
174
|
-
|
|
340
|
+
async function dispatchKeyEvent(cdp, type, keyDef, modifiers) {
|
|
341
|
+
await cdp.send('Input.dispatchKeyEvent', {
|
|
342
|
+
type,
|
|
343
|
+
code: keyDef.code,
|
|
344
|
+
key: keyDef.key,
|
|
345
|
+
windowsVirtualKeyCode: keyDef.keyCode,
|
|
346
|
+
nativeVirtualKeyCode: keyDef.keyCode,
|
|
347
|
+
modifiers,
|
|
348
|
+
});
|
|
175
349
|
}
|
|
176
350
|
//# sourceMappingURL=formFillHelpers.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formFillHelpers.js","sourceRoot":"","sources":["../../../src/commands/dom/formFillHelpers.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"formFillHelpers.js","sourceRoot":"","sources":["../../../src/commands/dom/formFillHelpers.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formInteraction.d.ts","sourceRoot":"","sources":["../../../src/commands/dom/formInteraction.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"formInteraction.d.ts","sourceRoot":"","sources":["../../../src/commands/dom/formInteraction.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqEzC;;;;;;;;;;GAUG;AACH,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkOtE"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Form interaction commands for filling inputs, clicking buttons, and submitting forms.
|
|
3
3
|
*/
|
|
4
|
-
import { fillElement, clickElement } from '../../commands/dom/formFillHelpers.js';
|
|
4
|
+
import { fillElement, clickElement, pressKeyElement, waitForActionStability, } from '../../commands/dom/formFillHelpers.js';
|
|
5
5
|
import { submitForm } from '../../commands/dom/formSubmitHelpers.js';
|
|
6
|
+
import { resolveElementTarget } from '../../commands/dom/helpers.js';
|
|
6
7
|
import { runCommand } from '../../commands/shared/CommandRunner.js';
|
|
7
8
|
import { jsonOption } from '../../commands/shared/commonOptions.js';
|
|
8
9
|
import { CommandError } from '../../ui/errors/index.js';
|
|
@@ -63,20 +64,30 @@ export function registerFormInteractionCommands(program) {
|
|
|
63
64
|
}
|
|
64
65
|
domCommand
|
|
65
66
|
.command('fill')
|
|
66
|
-
.description('Fill a form field with a value (React-compatible)')
|
|
67
|
-
.argument('<
|
|
67
|
+
.description('Fill a form field with a value (React-compatible, waits for stability)')
|
|
68
|
+
.argument('<selectorOrIndex>', 'CSS selector or numeric index from query results (0-based)')
|
|
68
69
|
.argument('<value>', 'Value to fill')
|
|
69
70
|
.option('--index <n>', 'Element index if selector matches multiple (1-based)', parseInt)
|
|
70
71
|
.option('--no-blur', 'Do not blur after filling (keeps focus on element)')
|
|
72
|
+
.option('--no-wait', 'Skip waiting for network stability after fill')
|
|
71
73
|
.addOption(jsonOption)
|
|
72
|
-
.action(async (
|
|
74
|
+
.action(async (selectorOrIndex, value, options) => {
|
|
73
75
|
await runCommand(async () => {
|
|
76
|
+
const target = await resolveElementTarget(selectorOrIndex, options.index);
|
|
77
|
+
if (!target.success) {
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
error: target.error ?? 'Failed to resolve element target',
|
|
81
|
+
exitCode: target.exitCode ?? EXIT_CODES.INVALID_ARGUMENTS,
|
|
82
|
+
suggestion: target.suggestion,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
74
85
|
return await withCDPConnection(async (cdp) => {
|
|
75
86
|
const fillOptions = filterDefined({
|
|
76
|
-
index:
|
|
87
|
+
index: target.index,
|
|
77
88
|
blur: options.blur,
|
|
78
89
|
});
|
|
79
|
-
const result = await fillElement(cdp, selector, value, fillOptions);
|
|
90
|
+
const result = await fillElement(cdp, target.selector, value, fillOptions);
|
|
80
91
|
if (!result.success) {
|
|
81
92
|
return {
|
|
82
93
|
success: false,
|
|
@@ -86,23 +97,36 @@ export function registerFormInteractionCommands(program) {
|
|
|
86
97
|
: EXIT_CODES.INVALID_ARGUMENTS,
|
|
87
98
|
};
|
|
88
99
|
}
|
|
100
|
+
if (options.wait !== false) {
|
|
101
|
+
await waitForActionStability(cdp);
|
|
102
|
+
}
|
|
89
103
|
return { success: true, data: result };
|
|
90
104
|
});
|
|
91
105
|
}, options, formatFillOutput);
|
|
92
106
|
});
|
|
93
107
|
domCommand
|
|
94
108
|
.command('click')
|
|
95
|
-
.description('Click an element')
|
|
96
|
-
.argument('<
|
|
109
|
+
.description('Click an element and wait for stability (accepts selector or index)')
|
|
110
|
+
.argument('<selectorOrIndex>', 'CSS selector or numeric index from query results (0-based)')
|
|
97
111
|
.option('--index <n>', 'Element index if selector matches multiple (1-based)', parseInt)
|
|
112
|
+
.option('--no-wait', 'Skip waiting for network stability after click')
|
|
98
113
|
.addOption(jsonOption)
|
|
99
|
-
.action(async (
|
|
114
|
+
.action(async (selectorOrIndex, options) => {
|
|
100
115
|
await runCommand(async () => {
|
|
116
|
+
const target = await resolveElementTarget(selectorOrIndex, options.index);
|
|
117
|
+
if (!target.success) {
|
|
118
|
+
return {
|
|
119
|
+
success: false,
|
|
120
|
+
error: target.error ?? 'Failed to resolve element target',
|
|
121
|
+
exitCode: target.exitCode ?? EXIT_CODES.INVALID_ARGUMENTS,
|
|
122
|
+
suggestion: target.suggestion,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
101
125
|
return await withCDPConnection(async (cdp) => {
|
|
102
126
|
const clickOptions = filterDefined({
|
|
103
|
-
index:
|
|
127
|
+
index: target.index,
|
|
104
128
|
});
|
|
105
|
-
const result = await clickElement(cdp, selector, clickOptions);
|
|
129
|
+
const result = await clickElement(cdp, target.selector, clickOptions);
|
|
106
130
|
if (!result.success) {
|
|
107
131
|
return {
|
|
108
132
|
success: false,
|
|
@@ -112,6 +136,9 @@ export function registerFormInteractionCommands(program) {
|
|
|
112
136
|
: EXIT_CODES.INVALID_ARGUMENTS,
|
|
113
137
|
};
|
|
114
138
|
}
|
|
139
|
+
if (options.wait !== false) {
|
|
140
|
+
await waitForActionStability(cdp);
|
|
141
|
+
}
|
|
115
142
|
return { success: true, data: result };
|
|
116
143
|
});
|
|
117
144
|
}, options, formatClickOutput);
|
|
@@ -119,22 +146,31 @@ export function registerFormInteractionCommands(program) {
|
|
|
119
146
|
domCommand
|
|
120
147
|
.command('submit')
|
|
121
148
|
.description('Submit a form by clicking submit button and waiting for completion')
|
|
122
|
-
.argument('<
|
|
149
|
+
.argument('<selectorOrIndex>', 'CSS selector or numeric index from query results (0-based)')
|
|
123
150
|
.option('--index <n>', 'Element index if selector matches multiple (1-based)', parseInt)
|
|
124
151
|
.option('--wait-navigation', 'Wait for page navigation after submit')
|
|
125
152
|
.option('--wait-network <ms>', 'Wait for network idle after submit (milliseconds)', '1000')
|
|
126
153
|
.option('--timeout <ms>', 'Maximum time to wait (milliseconds)', '10000')
|
|
127
154
|
.addOption(jsonOption)
|
|
128
|
-
.action(async (
|
|
155
|
+
.action(async (selectorOrIndex, options) => {
|
|
129
156
|
await runCommand(async () => {
|
|
157
|
+
const target = await resolveElementTarget(selectorOrIndex, options.index);
|
|
158
|
+
if (!target.success) {
|
|
159
|
+
return {
|
|
160
|
+
success: false,
|
|
161
|
+
error: target.error ?? 'Failed to resolve element target',
|
|
162
|
+
exitCode: target.exitCode ?? EXIT_CODES.INVALID_ARGUMENTS,
|
|
163
|
+
suggestion: target.suggestion,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
130
166
|
return await withCDPConnection(async (cdp) => {
|
|
131
167
|
const submitOptions = filterDefined({
|
|
132
|
-
index:
|
|
168
|
+
index: target.index,
|
|
133
169
|
waitNavigation: options.waitNavigation,
|
|
134
170
|
waitNetwork: parseInt(options.waitNetwork, 10),
|
|
135
171
|
timeout: parseInt(options.timeout, 10),
|
|
136
172
|
});
|
|
137
|
-
const result = await submitForm(cdp, selector, submitOptions);
|
|
173
|
+
const result = await submitForm(cdp, target.selector, submitOptions);
|
|
138
174
|
if (!result.success) {
|
|
139
175
|
return {
|
|
140
176
|
success: false,
|
|
@@ -150,6 +186,50 @@ export function registerFormInteractionCommands(program) {
|
|
|
150
186
|
});
|
|
151
187
|
}, options, formatSubmitOutput);
|
|
152
188
|
});
|
|
189
|
+
domCommand
|
|
190
|
+
.command('pressKey')
|
|
191
|
+
.description('Press a key on an element (for Enter-to-submit, keyboard navigation)')
|
|
192
|
+
.argument('<selectorOrIndex>', 'CSS selector or numeric index from query results (0-based)')
|
|
193
|
+
.argument('<key>', 'Key to press (Enter, Tab, Escape, Space, ArrowUp, etc.)')
|
|
194
|
+
.option('--index <n>', 'Element index if selector matches multiple (1-based)', parseInt)
|
|
195
|
+
.option('--times <n>', 'Press key multiple times (default: 1)', parseInt)
|
|
196
|
+
.option('--modifiers <mods>', 'Modifier keys: shift,ctrl,alt,meta (comma-separated)')
|
|
197
|
+
.option('--no-wait', 'Skip waiting for network stability after key press')
|
|
198
|
+
.addOption(jsonOption)
|
|
199
|
+
.action(async (selectorOrIndex, key, options) => {
|
|
200
|
+
await runCommand(async () => {
|
|
201
|
+
const target = await resolveElementTarget(selectorOrIndex, options.index);
|
|
202
|
+
if (!target.success) {
|
|
203
|
+
return {
|
|
204
|
+
success: false,
|
|
205
|
+
error: target.error ?? 'Failed to resolve element target',
|
|
206
|
+
exitCode: target.exitCode ?? EXIT_CODES.INVALID_ARGUMENTS,
|
|
207
|
+
suggestion: target.suggestion,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
return await withCDPConnection(async (cdp) => {
|
|
211
|
+
const pressKeyOptions = filterDefined({
|
|
212
|
+
index: target.index,
|
|
213
|
+
times: options.times,
|
|
214
|
+
modifiers: options.modifiers,
|
|
215
|
+
});
|
|
216
|
+
const result = await pressKeyElement(cdp, target.selector, key, pressKeyOptions);
|
|
217
|
+
if (!result.success) {
|
|
218
|
+
return {
|
|
219
|
+
success: false,
|
|
220
|
+
error: result.error ?? 'Failed to press key',
|
|
221
|
+
exitCode: result.error?.includes('not found')
|
|
222
|
+
? EXIT_CODES.RESOURCE_NOT_FOUND
|
|
223
|
+
: EXIT_CODES.INVALID_ARGUMENTS,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
if (options.wait !== false) {
|
|
227
|
+
await waitForActionStability(cdp);
|
|
228
|
+
}
|
|
229
|
+
return { success: true, data: result };
|
|
230
|
+
});
|
|
231
|
+
}, options, formatPressKeyOutput);
|
|
232
|
+
});
|
|
153
233
|
}
|
|
154
234
|
/**
|
|
155
235
|
* Format fill command output for human-readable display.
|
|
@@ -231,4 +311,37 @@ function formatSubmitOutput(result) {
|
|
|
231
311
|
]);
|
|
232
312
|
return fmt.build();
|
|
233
313
|
}
|
|
314
|
+
/**
|
|
315
|
+
* Format pressKey command output for human-readable display.
|
|
316
|
+
*
|
|
317
|
+
* @param result - Press key result
|
|
318
|
+
* @returns Formatted string
|
|
319
|
+
*/
|
|
320
|
+
function formatPressKeyOutput(result) {
|
|
321
|
+
const fmt = new OutputFormatter();
|
|
322
|
+
fmt.text('✓ Key Pressed');
|
|
323
|
+
fmt.blank();
|
|
324
|
+
const details = [
|
|
325
|
+
['Key', result.key ?? 'unknown'],
|
|
326
|
+
['Selector', result.selector ?? 'unknown'],
|
|
327
|
+
['Element Type', result.elementType ?? 'unknown'],
|
|
328
|
+
];
|
|
329
|
+
if (result.times && result.times > 1) {
|
|
330
|
+
details.push(['Times', result.times.toString()]);
|
|
331
|
+
}
|
|
332
|
+
if (result.modifiers && result.modifiers > 0) {
|
|
333
|
+
const mods = [];
|
|
334
|
+
if (result.modifiers & 1)
|
|
335
|
+
mods.push('Shift');
|
|
336
|
+
if (result.modifiers & 2)
|
|
337
|
+
mods.push('Ctrl');
|
|
338
|
+
if (result.modifiers & 4)
|
|
339
|
+
mods.push('Alt');
|
|
340
|
+
if (result.modifiers & 8)
|
|
341
|
+
mods.push('Meta');
|
|
342
|
+
details.push(['Modifiers', mods.join('+')]);
|
|
343
|
+
}
|
|
344
|
+
fmt.keyValueList(details, 15);
|
|
345
|
+
return fmt.build();
|
|
346
|
+
}
|
|
234
347
|
//# sourceMappingURL=formInteraction.js.map
|