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
- // Focus on the element first
121
- await element.focus();
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
- if (el) {
126
- el.select();
127
- el.value = '';
128
- }
124
+ return el?.tagName.toLowerCase() === 'select';
129
125
  }, usedSelector);
130
- // Type the new text
131
- await pageInstance.type(usedSelector, text, { delay });
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(2); // Clear + JavaScript type
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-browser-mcp-server",
3
- "version": "2.4.3",
3
+ "version": "2.5.1",
4
4
  "description": "MCP server for brave-real-browser",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",