brave-real-browser-mcp-server 2.9.1 → 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:
|
|
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(
|
|
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(
|
|
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:
|
|
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(
|
|
94
|
-
expect(result.content[0].text).toContain(
|
|
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:
|
|
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:
|
|
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(
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
+
};
|