brave-real-browser-mcp-server 2.9.2 → 2.9.3

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.
@@ -9,6 +9,11 @@
9
9
  */
10
10
  import { describe, it, expect, beforeEach, vi } from 'vitest';
11
11
  import { handleNavigate, handleWait } from './navigation-handlers.js';
12
+ // Test constants
13
+ const TEST_URLS = {
14
+ BASIC: 'https://example.com',
15
+ FORM: 'https://httpbin.org/forms/post'
16
+ };
12
17
  // Mock all external dependencies
13
18
  vi.mock('../browser-manager', () => ({
14
19
  getBrowserInstance: vi.fn(),
@@ -69,33 +74,33 @@ describe('Navigation Handlers', () => {
69
74
  describe('Successful Navigation', () => {
70
75
  it('should navigate to URL successfully', async () => {
71
76
  // Arrange: Basic navigation args
72
- const args = { url: 'https://example.com' };
77
+ const args = { url: TEST_URLS.BASIC };
73
78
  mockPageInstance.goto.mockResolvedValue(undefined);
74
79
  // Act: Navigate to URL
75
80
  const result = await handleNavigate(args);
76
81
  // Assert: Should navigate successfully
77
- expect(mockPageInstance.goto).toHaveBeenCalledWith('https://example.com', { waitUntil: 'domcontentloaded', timeout: 60000 });
82
+ expect(mockPageInstance.goto).toHaveBeenCalledWith(TEST_URLS.BASIC, { waitUntil: 'domcontentloaded', timeout: 60000 });
78
83
  expect(mockWorkflowValidation.validateWorkflow).toHaveBeenCalledWith('navigate', args);
79
84
  expect(mockWorkflowValidation.recordExecution).toHaveBeenCalledWith('navigate', args, true);
80
85
  expect(result).toHaveProperty('content');
81
86
  expect(result.content[0].type).toBe('text');
82
- expect(result.content[0].text).toContain('Successfully navigated to https://example.com');
87
+ expect(result.content[0].text).toContain(`Successfully navigated to ${TEST_URLS.BASIC}`);
83
88
  expect(result.content[0].text).toContain('Workflow Status: Page loaded');
84
89
  expect(result.content[0].text).toContain('Next step: Use get_content');
85
90
  });
86
91
  it('should navigate with custom waitUntil option', async () => {
87
92
  // Arrange: Navigation with custom wait condition
88
- const args = { url: 'https://example.com', waitUntil: 'load' };
93
+ const args = { url: TEST_URLS.BASIC, waitUntil: 'load' };
89
94
  mockPageInstance.goto.mockResolvedValue(undefined);
90
95
  // Act: Navigate with custom wait condition
91
96
  const result = await handleNavigate(args);
92
97
  // Assert: Should use custom waitUntil
93
- expect(mockPageInstance.goto).toHaveBeenCalledWith('https://example.com', { waitUntil: 'load', timeout: 60000 });
94
- expect(result.content[0].text).toContain('Successfully navigated to https://example.com');
98
+ expect(mockPageInstance.goto).toHaveBeenCalledWith(TEST_URLS.BASIC, { waitUntil: 'load', timeout: 60000 });
99
+ expect(result.content[0].text).toContain(`Successfully navigated to ${TEST_URLS.BASIC}`);
95
100
  });
96
101
  it('should include comprehensive workflow guidance', async () => {
97
102
  // Arrange: Successful navigation
98
- const args = { url: 'https://test.com' };
103
+ const args = { url: TEST_URLS.BASIC };
99
104
  mockPageInstance.goto.mockResolvedValue(undefined);
100
105
  // Act: Navigate
101
106
  const result = await handleNavigate(args);
@@ -109,7 +114,7 @@ describe('Navigation Handlers', () => {
109
114
  describe('Navigation Retry Logic', () => {
110
115
  it('should retry navigation on failure and succeed', async () => {
111
116
  // Arrange: Navigation fails first time, succeeds second time
112
- const args = { url: 'https://retry.com' };
117
+ const args = { url: TEST_URLS.BASIC };
113
118
  mockPageInstance.goto
114
119
  .mockRejectedValueOnce(new Error('Network error'))
115
120
  .mockResolvedValueOnce(undefined);
@@ -117,11 +122,11 @@ describe('Navigation Handlers', () => {
117
122
  const result = await handleNavigate(args);
118
123
  // Assert: Should retry and succeed
119
124
  expect(mockPageInstance.goto).toHaveBeenCalledTimes(2);
120
- expect(result.content[0].text).toContain('Successfully navigated to https://retry.com');
125
+ expect(result.content[0].text).toContain(`Successfully navigated to ${TEST_URLS.BASIC}`);
121
126
  });
122
127
  it('should retry navigation multiple times before giving up', async () => {
123
128
  // Arrange: Navigation fails all attempts
124
- const args = { url: 'https://fail.com' };
129
+ const args = { url: TEST_URLS.BASIC };
125
130
  const networkError = new Error('Persistent network error');
126
131
  mockPageInstance.goto.mockRejectedValue(networkError);
127
132
  // Act & Assert: Should retry 3 times then fail
@@ -131,7 +136,7 @@ describe('Navigation Handlers', () => {
131
136
  });
132
137
  it('should use exponential backoff between retries', async () => {
133
138
  // Arrange: Navigation fails with retries
134
- const args = { url: 'https://backoff.com' };
139
+ const args = { url: TEST_URLS.BASIC };
135
140
  mockPageInstance.goto.mockRejectedValue(new Error('Timeout'));
136
141
  // Act: Attempt navigation (will fail after retries)
137
142
  try {
@@ -148,14 +153,14 @@ describe('Navigation Handlers', () => {
148
153
  describe('Navigation Error Handling', () => {
149
154
  it('should throw error when browser not initialized', async () => {
150
155
  // Arrange: No page instance
151
- const args = { url: 'https://example.com' };
156
+ const args = { url: TEST_URLS.BASIC };
152
157
  mockBrowserManager.getPageInstance.mockReturnValue(null);
153
158
  // Act & Assert: Should throw browser not initialized error
154
159
  await expect(handleNavigate(args)).rejects.toThrow('Browser not initialized. Call browser_init first.');
155
160
  });
156
161
  it('should handle workflow validation failure', async () => {
157
162
  // Arrange: Invalid workflow state
158
- const args = { url: 'https://example.com' };
163
+ const args = { url: TEST_URLS.BASIC };
159
164
  mockWorkflowValidation.validateWorkflow.mockReturnValue({
160
165
  isValid: false,
161
166
  errorMessage: 'Cannot navigate in current state',
@@ -166,7 +171,7 @@ describe('Navigation Handlers', () => {
166
171
  });
167
172
  it('should handle timeout errors from withTimeout wrapper', async () => {
168
173
  // Arrange: Navigation that times out
169
- const args = { url: 'https://timeout.com' };
174
+ const args = { url: TEST_URLS.BASIC };
170
175
  mockSystemUtils.withTimeout.mockImplementation(async (operation, timeout, context) => {
171
176
  throw new Error(`Operation timed out after ${timeout}ms in context: ${context}`);
172
177
  });
@@ -310,7 +315,7 @@ describe('Navigation Handlers', () => {
310
315
  describe('Workflow Validation Integration', () => {
311
316
  it('should validate workflow before navigation operations', async () => {
312
317
  // Arrange: Valid navigation request
313
- const args = { url: 'https://example.com' };
318
+ const args = { url: TEST_URLS.BASIC };
314
319
  mockPageInstance.goto.mockResolvedValue(undefined);
315
320
  // Act: Execute navigation
316
321
  await handleNavigate(args);
@@ -328,7 +333,7 @@ describe('Navigation Handlers', () => {
328
333
  });
329
334
  it('should record successful executions', async () => {
330
335
  // Arrange: Successful operation
331
- const args = { url: 'https://success.com' };
336
+ const args = { url: TEST_URLS.BASIC };
332
337
  mockPageInstance.goto.mockResolvedValue(undefined);
333
338
  // Act: Execute successful operation
334
339
  await handleNavigate(args);
@@ -352,7 +357,7 @@ describe('Navigation Handlers', () => {
352
357
  describe('System Integration', () => {
353
358
  it('should use error handling wrapper for navigation', async () => {
354
359
  // Arrange: Navigation operation
355
- const args = { url: 'https://example.com' };
360
+ const args = { url: TEST_URLS.BASIC };
356
361
  mockPageInstance.goto.mockResolvedValue(undefined);
357
362
  // Act: Execute navigation
358
363
  await handleNavigate(args);
@@ -370,7 +375,7 @@ describe('Navigation Handlers', () => {
370
375
  });
371
376
  it('should use timeout wrapper for navigation', async () => {
372
377
  // Arrange: Navigation operation
373
- const args = { url: 'https://example.com' };
378
+ const args = { url: TEST_URLS.BASIC };
374
379
  mockPageInstance.goto.mockResolvedValue(undefined);
375
380
  // Act: Execute navigation
376
381
  await handleNavigate(args);
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Central Test Configuration
3
+ * Use these constants across all test files for consistency
4
+ */
5
+ export const TEST_URLS = {
6
+ // CAPTCHA Testing URLs
7
+ CAPTCHA: {
8
+ CLOUDFLARE: 'https://nopecha.com/demo/cloudflare',
9
+ ECOURTS_INDIA: 'https://services.ecourts.gov.in/ecourtindia_v6/?p=casestatus/index&app_token=22e7493ca224682349cf0986dd144491a950819d30918b8e319ab0d39618f847',
10
+ RECAPTCHA: 'https://nopecha.com/demo/recaptcha',
11
+ HCAPTCHA: 'https://nopecha.com/demo/hcaptcha',
12
+ TURNSTILE: 'https://nopecha.com/demo/turnstile',
13
+ },
14
+ // General Content Testing
15
+ GENERAL: {
16
+ WIKIPEDIA: 'https://en.wikipedia.org/wiki/Web_scraping',
17
+ IMDB: 'https://www.imdb.com/',
18
+ GITHUB: 'https://github.com/',
19
+ EXAMPLE: 'https://example.com',
20
+ },
21
+ // API Discovery Testing
22
+ API: {
23
+ JSONPLACEHOLDER: 'https://jsonplaceholder.typicode.com',
24
+ REQRES: 'https://reqres.in/',
25
+ },
26
+ // E-commerce Testing
27
+ ECOMMERCE: {
28
+ AMAZON: 'https://www.amazon.com',
29
+ },
30
+ // Local Testing
31
+ LOCAL: {
32
+ LOCALHOST: 'http://localhost:3000',
33
+ FILE: 'file:///test.html',
34
+ },
35
+ };
36
+ export const TEST_SELECTORS = {
37
+ // Common selectors for testing
38
+ WIKIPEDIA: {
39
+ HEADING: '#firstHeading',
40
+ CONTENT: '#mw-content-text',
41
+ TOC: '#toc',
42
+ LINKS: 'a[href]',
43
+ IMAGES: 'img',
44
+ TABLES: 'table.wikitable',
45
+ },
46
+ ECOURTS: {
47
+ CAPTCHA_IMAGE: 'img[src*="captcha"]',
48
+ CAPTCHA_INPUT: 'input[name*="captcha" i]',
49
+ STATE_SELECT: 'select[name="state"]',
50
+ SEARCH_BUTTON: 'button[type="submit"]',
51
+ },
52
+ CLOUDFLARE: {
53
+ CHALLENGE: 'div[id^="cf-chl-widget"]',
54
+ IFRAME: 'iframe[src*="challenges.cloudflare.com"]',
55
+ VERIFY_TEXT: 'p:contains("Verifying")',
56
+ },
57
+ COMMON: {
58
+ HEADING: 'h1',
59
+ PARAGRAPH: 'p',
60
+ LINK: 'a',
61
+ IMAGE: 'img',
62
+ BUTTON: 'button',
63
+ INPUT: 'input',
64
+ },
65
+ };
66
+ export const TEST_TIMEOUTS = {
67
+ SHORT: 5000,
68
+ MEDIUM: 10000,
69
+ LONG: 30000,
70
+ CAPTCHA: 60000,
71
+ };
72
+ export const TEST_EXPECTATIONS = {
73
+ WIKIPEDIA: {
74
+ MIN_IMAGES: 5,
75
+ MIN_LINKS: 100,
76
+ HAS_TOC: true,
77
+ },
78
+ ECOURTS: {
79
+ HAS_CAPTCHA: true,
80
+ HAS_STATE_SELECT: true,
81
+ },
82
+ CLOUDFLARE: {
83
+ HAS_CHALLENGE: true,
84
+ VERIFICATION_TEXT: 'Verifying you are human',
85
+ },
86
+ };
87
+ export const TEST_DATA = {
88
+ // Sample data for testing
89
+ SENTIMENT: {
90
+ POSITIVE: 'This is an amazing product! I absolutely love it. Highly recommended!',
91
+ NEGATIVE: 'Terrible experience. Very disappointed. Would not recommend.',
92
+ NEUTRAL: 'The product arrived on time. It works as described.',
93
+ },
94
+ TRANSLATION: {
95
+ FRENCH: 'Bonjour, comment allez-vous? Je suis très heureux de vous rencontrer.',
96
+ SPANISH: 'Hola, ¿cómo estás? Estoy muy feliz de conocerte.',
97
+ GERMAN: 'Hallo, wie geht es dir? Ich bin sehr glücklich, dich kennenzulernen.',
98
+ },
99
+ SEARCH: {
100
+ PARTY_NAME: 'Ramesh Kumar',
101
+ CASE_NUMBER: '123/2024',
102
+ YEAR: '2024',
103
+ },
104
+ };
105
+ export default {
106
+ TEST_URLS,
107
+ TEST_SELECTORS,
108
+ TEST_TIMEOUTS,
109
+ TEST_EXPECTATIONS,
110
+ TEST_DATA,
111
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brave-real-browser-mcp-server",
3
- "version": "2.9.2",
3
+ "version": "2.9.3",
4
4
  "description": "MCP server for brave-real-browser",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",