brave-real-browser-mcp-server 2.4.3 → 2.5.1
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.
|
@@ -3,6 +3,7 @@ import { withErrorHandling } from '../system-utils.js';
|
|
|
3
3
|
import { validateWorkflow, recordExecution, workflowValidator } from '../workflow-validation.js';
|
|
4
4
|
import { selfHealingLocators } from '../self-healing-locators.js';
|
|
5
5
|
import { randomScroll } from '../stealth-actions.js';
|
|
6
|
+
import { setTimeout as sleep } from 'node:timers/promises';
|
|
6
7
|
// Click handler
|
|
7
8
|
export async function handleClick(args) {
|
|
8
9
|
return await withWorkflowValidation('click', args, async () => {
|
|
@@ -117,18 +118,62 @@ export async function handleType(args) {
|
|
|
117
118
|
try {
|
|
118
119
|
// Wait for element to be ready and interactable
|
|
119
120
|
await pageInstance.waitForSelector(usedSelector, { timeout: 5000 });
|
|
120
|
-
//
|
|
121
|
-
await
|
|
122
|
-
// Clear existing content
|
|
123
|
-
await pageInstance.evaluate((sel) => {
|
|
121
|
+
// Check if element is a dropdown/select
|
|
122
|
+
const isDropdown = await pageInstance.evaluate((sel) => {
|
|
124
123
|
const el = document.querySelector(sel);
|
|
125
|
-
|
|
126
|
-
el.select();
|
|
127
|
-
el.value = '';
|
|
128
|
-
}
|
|
124
|
+
return el?.tagName.toLowerCase() === 'select';
|
|
129
125
|
}, usedSelector);
|
|
130
|
-
|
|
131
|
-
|
|
126
|
+
if (isDropdown) {
|
|
127
|
+
// Handle dropdown selection with proper event triggering
|
|
128
|
+
await pageInstance.evaluate((sel, value) => {
|
|
129
|
+
const selectEl = document.querySelector(sel);
|
|
130
|
+
if (!selectEl)
|
|
131
|
+
return;
|
|
132
|
+
// Human-like behavior: Focus first
|
|
133
|
+
selectEl.focus();
|
|
134
|
+
// Find option by text or value
|
|
135
|
+
const options = Array.from(selectEl.options);
|
|
136
|
+
const matchedOption = options.find(opt => opt.text.toLowerCase().includes(value.toLowerCase()) ||
|
|
137
|
+
opt.value.toLowerCase().includes(value.toLowerCase()));
|
|
138
|
+
if (matchedOption) {
|
|
139
|
+
// Set the value
|
|
140
|
+
selectEl.value = matchedOption.value;
|
|
141
|
+
// Fire all relevant events for dynamic dropdowns
|
|
142
|
+
selectEl.dispatchEvent(new Event('input', { bubbles: true }));
|
|
143
|
+
selectEl.dispatchEvent(new Event('change', { bubbles: true }));
|
|
144
|
+
selectEl.dispatchEvent(new Event('blur', { bubbles: true }));
|
|
145
|
+
// Trigger jQuery change event if jQuery exists
|
|
146
|
+
if (typeof window.jQuery !== 'undefined') {
|
|
147
|
+
window.jQuery(selectEl).trigger('change');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}, usedSelector, text);
|
|
151
|
+
// Wait a bit for dependent dropdowns to load
|
|
152
|
+
await sleep(1500);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
// Regular input field
|
|
156
|
+
// Focus on the element first
|
|
157
|
+
await element.focus();
|
|
158
|
+
// Clear existing content
|
|
159
|
+
await pageInstance.evaluate((sel) => {
|
|
160
|
+
const el = document.querySelector(sel);
|
|
161
|
+
if (el) {
|
|
162
|
+
el.select();
|
|
163
|
+
el.value = '';
|
|
164
|
+
}
|
|
165
|
+
}, usedSelector);
|
|
166
|
+
// Type the new text with human-like delay
|
|
167
|
+
await pageInstance.type(usedSelector, text, { delay });
|
|
168
|
+
// Fire events for dynamic forms
|
|
169
|
+
await pageInstance.evaluate((sel) => {
|
|
170
|
+
const el = document.querySelector(sel);
|
|
171
|
+
if (el) {
|
|
172
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
173
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
174
|
+
}
|
|
175
|
+
}, usedSelector);
|
|
176
|
+
}
|
|
132
177
|
return {
|
|
133
178
|
content: [
|
|
134
179
|
{
|
|
@@ -251,13 +251,14 @@ describe('Interaction Handlers', () => {
|
|
|
251
251
|
const args = { selector: 'input', text: 'fallback text' };
|
|
252
252
|
mockPageInstance.type.mockRejectedValue(new Error('Type failed'));
|
|
253
253
|
mockPageInstance.evaluate
|
|
254
|
+
.mockResolvedValueOnce(false) // Check dropdown
|
|
254
255
|
.mockResolvedValueOnce(undefined) // Clear content
|
|
255
256
|
.mockResolvedValueOnce(undefined); // JavaScript typing
|
|
256
257
|
// Act: Type text
|
|
257
258
|
const result = await handleType(args);
|
|
258
259
|
// Assert: Should use JavaScript fallback
|
|
259
260
|
expect(mockPageInstance.type).toHaveBeenCalled();
|
|
260
|
-
expect(mockPageInstance.evaluate).toHaveBeenCalledTimes(
|
|
261
|
+
expect(mockPageInstance.evaluate).toHaveBeenCalledTimes(3); // Dropdown check + Clear + JavaScript type
|
|
261
262
|
expect(result.content[0].text).toContain('Typed text using JavaScript fallback');
|
|
262
263
|
});
|
|
263
264
|
});
|
|
@@ -275,6 +276,7 @@ describe('Interaction Handlers', () => {
|
|
|
275
276
|
const args = { selector: 'input', text: 'test' };
|
|
276
277
|
mockPageInstance.type.mockRejectedValue(new Error('Type operation failed'));
|
|
277
278
|
mockPageInstance.evaluate
|
|
279
|
+
.mockResolvedValueOnce(false) // Check dropdown
|
|
278
280
|
.mockResolvedValueOnce(undefined) // Clear content succeeds
|
|
279
281
|
.mockRejectedValueOnce(new Error('JavaScript type failed')); // JavaScript type fails
|
|
280
282
|
// Act & Assert: Should throw combined error
|