brave-real-browser-mcp-server 2.1.2 ā 2.1.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.
- package/dist/errors/workflow-wrapper.js +11 -6
- package/dist/handlers/file-handlers.js +6 -4
- package/dist/handlers/navigation-handlers.js +1 -1
- package/package.json +1 -1
- package/dist/browser-manager.test.js +0 -395
- package/dist/content-strategy.test.js +0 -525
- package/dist/handlers/browser-handlers.test.js +0 -337
- package/dist/handlers/content-handlers.test.js +0 -457
- package/dist/handlers/file-handlers.test.js +0 -289
- package/dist/handlers/interaction-handlers.test.js +0 -440
- package/dist/handlers/navigation-handlers.test.js +0 -383
- package/dist/token-management.test.js +0 -428
- package/dist/workflow-validation.test.js +0 -236
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* This module provides workflow validation that integrates with the centralized error system.
|
|
5
5
|
*/
|
|
6
6
|
import { validateWorkflow, recordExecution, workflowValidator } from '../workflow-validation.js';
|
|
7
|
-
import { categorizeError,
|
|
7
|
+
import { categorizeError, MCPError } from './index.js';
|
|
8
8
|
/**
|
|
9
9
|
* Wrapper that validates workflow before executing an operation
|
|
10
10
|
* and properly categorizes any errors that occur
|
|
@@ -13,13 +13,18 @@ export async function withWorkflowValidation(toolName, args, operation) {
|
|
|
13
13
|
// Validate workflow state before execution
|
|
14
14
|
const validation = validateWorkflow(toolName, args);
|
|
15
15
|
if (!validation.isValid) {
|
|
16
|
-
//
|
|
16
|
+
// Use validation error message if available, otherwise create generic error
|
|
17
|
+
let errorMessage = validation.errorMessage || `Workflow violation: Cannot execute '${toolName}'`;
|
|
18
|
+
// Add suggested action if available
|
|
19
|
+
if (validation.suggestedAction) {
|
|
20
|
+
errorMessage += `\n\nš” Next Steps: ${validation.suggestedAction}`;
|
|
21
|
+
}
|
|
22
|
+
// Add workflow context
|
|
17
23
|
const workflowSummary = workflowValidator.getValidationSummary();
|
|
18
|
-
|
|
19
|
-
const error = createWorkflowViolationError(toolName, workflowSummary, suggestedAction);
|
|
24
|
+
errorMessage += `\n\nš ${workflowSummary}`;
|
|
20
25
|
// Record failed execution
|
|
21
|
-
recordExecution(toolName, args, false,
|
|
22
|
-
throw
|
|
26
|
+
recordExecution(toolName, args, false, errorMessage);
|
|
27
|
+
throw new Error(errorMessage);
|
|
23
28
|
}
|
|
24
29
|
try {
|
|
25
30
|
// Execute the operation
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { promises as fs } from 'fs';
|
|
2
2
|
import { dirname, extname, resolve } from 'path';
|
|
3
3
|
import TurndownService from 'turndown';
|
|
4
|
+
// Ensure proper Turndown instance creation
|
|
5
|
+
const TurndownConstructor = TurndownService.default || TurndownService;
|
|
4
6
|
import { getPageInstance } from '../browser-manager.js';
|
|
5
7
|
import { withErrorHandling } from '../system-utils.js';
|
|
6
8
|
import { validateWorkflow, recordExecution, workflowValidator } from '../workflow-validation.js';
|
|
@@ -28,7 +30,7 @@ function validateFilePath(filePath) {
|
|
|
28
30
|
// Configure Turndown service for optimal markdown conversion
|
|
29
31
|
function createTurndownService(formatOptions = {}) {
|
|
30
32
|
const { preserveLinks = true, headingStyle = 'atx', cleanupWhitespace = true } = formatOptions;
|
|
31
|
-
const turndownService = new
|
|
33
|
+
const turndownService = new TurndownConstructor({
|
|
32
34
|
headingStyle,
|
|
33
35
|
bulletListMarker: '-',
|
|
34
36
|
codeBlockStyle: 'fenced',
|
|
@@ -102,13 +104,13 @@ function cleanupMarkdownWhitespace(content) {
|
|
|
102
104
|
export async function handleSaveContentAsMarkdown(args) {
|
|
103
105
|
return await withWorkflowValidation('save_content_as_markdown', args, async () => {
|
|
104
106
|
return await withErrorHandling(async () => {
|
|
107
|
+
const { filePath, contentType = 'text', selector, formatOptions = {} } = args;
|
|
108
|
+
// Validate file path for security BEFORE any other operations
|
|
109
|
+
validateFilePath(filePath);
|
|
105
110
|
const pageInstance = getPageInstance();
|
|
106
111
|
if (!pageInstance) {
|
|
107
112
|
throw new Error('Browser not initialized. Call browser_init first.');
|
|
108
113
|
}
|
|
109
|
-
const { filePath, contentType = 'text', selector, formatOptions = {} } = args;
|
|
110
|
-
// Validate file path for security
|
|
111
|
-
validateFilePath(filePath);
|
|
112
114
|
// Ensure directory exists
|
|
113
115
|
const dirPath = dirname(filePath);
|
|
114
116
|
try {
|
|
@@ -9,7 +9,7 @@ export async function handleNavigate(args) {
|
|
|
9
9
|
if (!pageInstance) {
|
|
10
10
|
throw new Error('Browser not initialized. Call browser_init first.');
|
|
11
11
|
}
|
|
12
|
-
const { url, waitUntil = '
|
|
12
|
+
const { url, waitUntil = 'networkidle2' } = args;
|
|
13
13
|
console.error(`š Navigating to: ${url}`);
|
|
14
14
|
// Navigate with retry logic
|
|
15
15
|
let lastError = null;
|
package/package.json
CHANGED
|
@@ -1,395 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit Tests for Browser Manager
|
|
3
|
-
*
|
|
4
|
-
* Following TDD Red-Green-Refactor methodology with 2025 best practices:
|
|
5
|
-
* - AAA Pattern (Arrange-Act-Assert)
|
|
6
|
-
* - Behavior-focused testing with proper mocking
|
|
7
|
-
* - Error categorization and circuit breaker testing
|
|
8
|
-
* - Chrome detection and network utilities testing
|
|
9
|
-
*/
|
|
10
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
11
|
-
import * as fs from 'fs';
|
|
12
|
-
import * as net from 'net';
|
|
13
|
-
import { BrowserErrorType, categorizeError, withTimeout, isPortAvailable, testHostConnectivity, findAvailablePort, updateCircuitBreakerOnFailure, updateCircuitBreakerOnSuccess, isCircuitBreakerOpen, detectChromePath, validateSession, findAuthElements, getBrowserInstance, getPageInstance, getContentPriorityConfig, updateContentPriorityConfig, forceKillAllChromeProcesses } from './browser-manager.js';
|
|
14
|
-
// Mock external dependencies
|
|
15
|
-
vi.mock('fs');
|
|
16
|
-
vi.mock('net');
|
|
17
|
-
vi.mock('child_process');
|
|
18
|
-
vi.mock('brave-real-browser', () => ({
|
|
19
|
-
connect: vi.fn()
|
|
20
|
-
}));
|
|
21
|
-
describe('Browser Manager', () => {
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
vi.clearAllMocks();
|
|
24
|
-
vi.resetModules();
|
|
25
|
-
});
|
|
26
|
-
// Helper function to create mock server for port testing
|
|
27
|
-
const createMockServer = (shouldSucceed = true) => {
|
|
28
|
-
return {
|
|
29
|
-
listen: vi.fn((port, host, callback) => {
|
|
30
|
-
if (shouldSucceed) {
|
|
31
|
-
callback();
|
|
32
|
-
}
|
|
33
|
-
return createMockServer(shouldSucceed);
|
|
34
|
-
}),
|
|
35
|
-
close: vi.fn(),
|
|
36
|
-
once: vi.fn((event, callback) => {
|
|
37
|
-
if (event === 'close')
|
|
38
|
-
callback();
|
|
39
|
-
}),
|
|
40
|
-
on: vi.fn((event, callback) => {
|
|
41
|
-
if (!shouldSucceed && event === 'error') {
|
|
42
|
-
callback(new Error('EADDRINUSE'));
|
|
43
|
-
}
|
|
44
|
-
})
|
|
45
|
-
};
|
|
46
|
-
};
|
|
47
|
-
describe('Error Categorization', () => {
|
|
48
|
-
it('should categorize FRAME_DETACHED error correctly', () => {
|
|
49
|
-
// Arrange: Create error with frame detached message
|
|
50
|
-
const error = new Error('Navigating frame was detached');
|
|
51
|
-
// Act: Categorize the error
|
|
52
|
-
const result = categorizeError(error);
|
|
53
|
-
// Assert: Should return FRAME_DETACHED type
|
|
54
|
-
expect(result).toBe(BrowserErrorType.FRAME_DETACHED);
|
|
55
|
-
});
|
|
56
|
-
it('should categorize SESSION_CLOSED error correctly', () => {
|
|
57
|
-
// Arrange: Create error with session closed message
|
|
58
|
-
const error = new Error('Session closed');
|
|
59
|
-
// Act: Categorize the error
|
|
60
|
-
const result = categorizeError(error);
|
|
61
|
-
// Assert: Should return SESSION_CLOSED type
|
|
62
|
-
expect(result).toBe(BrowserErrorType.SESSION_CLOSED);
|
|
63
|
-
});
|
|
64
|
-
it('should categorize TARGET_CLOSED error correctly', () => {
|
|
65
|
-
// Arrange: Create error with target closed message
|
|
66
|
-
const error = new Error('Target closed');
|
|
67
|
-
// Act: Categorize the error
|
|
68
|
-
const result = categorizeError(error);
|
|
69
|
-
// Assert: Should return TARGET_CLOSED type
|
|
70
|
-
expect(result).toBe(BrowserErrorType.TARGET_CLOSED);
|
|
71
|
-
});
|
|
72
|
-
it('should categorize PROTOCOL_ERROR correctly', () => {
|
|
73
|
-
// Arrange: Create error with protocol error message
|
|
74
|
-
const error = new Error('Protocol error');
|
|
75
|
-
// Act: Categorize the error
|
|
76
|
-
const result = categorizeError(error);
|
|
77
|
-
// Assert: Should return PROTOCOL_ERROR type
|
|
78
|
-
expect(result).toBe(BrowserErrorType.PROTOCOL_ERROR);
|
|
79
|
-
});
|
|
80
|
-
it('should categorize NAVIGATION_TIMEOUT error correctly', () => {
|
|
81
|
-
// Arrange: Create error with navigation timeout message
|
|
82
|
-
const error = new Error('Navigation timeout exceeded');
|
|
83
|
-
// Act: Categorize the error
|
|
84
|
-
const result = categorizeError(error);
|
|
85
|
-
// Assert: Should return NAVIGATION_TIMEOUT type
|
|
86
|
-
expect(result).toBe(BrowserErrorType.NAVIGATION_TIMEOUT);
|
|
87
|
-
});
|
|
88
|
-
it('should categorize ELEMENT_NOT_FOUND error correctly', () => {
|
|
89
|
-
// Arrange: Create error with element not found message
|
|
90
|
-
const error = new Error('Element not found');
|
|
91
|
-
// Act: Categorize the error
|
|
92
|
-
const result = categorizeError(error);
|
|
93
|
-
// Assert: Should return ELEMENT_NOT_FOUND type
|
|
94
|
-
expect(result).toBe(BrowserErrorType.ELEMENT_NOT_FOUND);
|
|
95
|
-
});
|
|
96
|
-
it('should categorize unknown errors as UNKNOWN', () => {
|
|
97
|
-
// Arrange: Create error with unrecognized message
|
|
98
|
-
const error = new Error('Some random error message');
|
|
99
|
-
// Act: Categorize the error
|
|
100
|
-
const result = categorizeError(error);
|
|
101
|
-
// Assert: Should return UNKNOWN type
|
|
102
|
-
expect(result).toBe(BrowserErrorType.UNKNOWN);
|
|
103
|
-
});
|
|
104
|
-
it('should handle case-insensitive error message matching', () => {
|
|
105
|
-
// Arrange: Create error with uppercase message
|
|
106
|
-
const error = new Error('SESSION CLOSED');
|
|
107
|
-
// Act: Categorize the error
|
|
108
|
-
const result = categorizeError(error);
|
|
109
|
-
// Assert: Should still categorize correctly
|
|
110
|
-
expect(result).toBe(BrowserErrorType.SESSION_CLOSED);
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
describe('Timeout Wrapper', () => {
|
|
114
|
-
it('should resolve when operation completes within timeout', async () => {
|
|
115
|
-
// Arrange: Create operation that resolves quickly
|
|
116
|
-
const operation = vi.fn().mockResolvedValue('success');
|
|
117
|
-
// Act: Execute with timeout
|
|
118
|
-
const result = await withTimeout(operation, 1000, 'test-context');
|
|
119
|
-
// Assert: Should return operation result
|
|
120
|
-
expect(result).toBe('success');
|
|
121
|
-
expect(operation).toHaveBeenCalledOnce();
|
|
122
|
-
});
|
|
123
|
-
it('should reject when operation times out', async () => {
|
|
124
|
-
// Arrange: Create operation that never resolves
|
|
125
|
-
const operation = vi.fn().mockImplementation(() => new Promise(() => { }));
|
|
126
|
-
// Act & Assert: Should throw timeout error
|
|
127
|
-
await expect(withTimeout(operation, 100, 'test-context'))
|
|
128
|
-
.rejects.toThrow('Operation timed out after 100ms in context: test-context');
|
|
129
|
-
expect(operation).toHaveBeenCalledOnce();
|
|
130
|
-
});
|
|
131
|
-
it('should reject when operation throws error', async () => {
|
|
132
|
-
// Arrange: Create operation that throws
|
|
133
|
-
const operation = vi.fn().mockRejectedValue(new Error('operation failed'));
|
|
134
|
-
// Act & Assert: Should propagate operation error
|
|
135
|
-
await expect(withTimeout(operation, 1000, 'test-context'))
|
|
136
|
-
.rejects.toThrow('operation failed');
|
|
137
|
-
expect(operation).toHaveBeenCalledOnce();
|
|
138
|
-
});
|
|
139
|
-
it('should clear timeout when operation completes', async () => {
|
|
140
|
-
// Arrange: Create operation that resolves
|
|
141
|
-
const operation = vi.fn().mockResolvedValue('success');
|
|
142
|
-
const clearTimeoutSpy = vi.spyOn(global, 'clearTimeout');
|
|
143
|
-
// Act: Execute with timeout
|
|
144
|
-
await withTimeout(operation, 1000, 'test-context');
|
|
145
|
-
// Assert: Should clear timeout
|
|
146
|
-
expect(clearTimeoutSpy).toHaveBeenCalled();
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
describe('Port Availability', () => {
|
|
150
|
-
it('should return true when port is available', async () => {
|
|
151
|
-
// Arrange: Mock net.createServer to succeed
|
|
152
|
-
const mockServer = createMockServer(true);
|
|
153
|
-
vi.mocked(net.createServer).mockReturnValue(mockServer);
|
|
154
|
-
// Act: Check port availability
|
|
155
|
-
const result = await isPortAvailable(9222);
|
|
156
|
-
// Assert: Should return true
|
|
157
|
-
expect(result).toBe(true);
|
|
158
|
-
expect(mockServer.listen).toHaveBeenCalledWith(9222, '127.0.0.1', expect.any(Function));
|
|
159
|
-
});
|
|
160
|
-
it('should return false when port is not available', async () => {
|
|
161
|
-
// Arrange: Mock net.createServer to fail
|
|
162
|
-
const mockServer = createMockServer(false);
|
|
163
|
-
vi.mocked(net.createServer).mockReturnValue(mockServer);
|
|
164
|
-
// Act: Check port availability
|
|
165
|
-
const result = await isPortAvailable(9222);
|
|
166
|
-
// Assert: Should return false
|
|
167
|
-
expect(result).toBe(false);
|
|
168
|
-
});
|
|
169
|
-
it('should use custom host when provided', async () => {
|
|
170
|
-
// Arrange: Mock net.createServer to succeed
|
|
171
|
-
const mockServer = createMockServer(true);
|
|
172
|
-
vi.mocked(net.createServer).mockReturnValue(mockServer);
|
|
173
|
-
// Act: Check port availability with custom host
|
|
174
|
-
await isPortAvailable(9222, 'localhost');
|
|
175
|
-
// Assert: Should use custom host
|
|
176
|
-
expect(mockServer.listen).toHaveBeenCalledWith(9222, 'localhost', expect.any(Function));
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
describe('Host Connectivity Testing', () => {
|
|
180
|
-
it('should return connectivity results structure', async () => {
|
|
181
|
-
// Arrange & Act: Test host connectivity (real implementation)
|
|
182
|
-
const result = await testHostConnectivity();
|
|
183
|
-
// Assert: Should return expected structure regardless of actual connectivity
|
|
184
|
-
expect(result).toHaveProperty('localhost');
|
|
185
|
-
expect(result).toHaveProperty('ipv4');
|
|
186
|
-
expect(result).toHaveProperty('recommendedHost');
|
|
187
|
-
expect(typeof result.localhost).toBe('boolean');
|
|
188
|
-
expect(typeof result.ipv4).toBe('boolean');
|
|
189
|
-
expect(typeof result.recommendedHost).toBe('string');
|
|
190
|
-
expect(['localhost', '127.0.0.1']).toContain(result.recommendedHost);
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
describe('Available Port Finding', () => {
|
|
194
|
-
it('should return a valid port number or null', async () => {
|
|
195
|
-
// Arrange & Act: Find available port in a reasonable range
|
|
196
|
-
const result = await findAvailablePort(9222, 9224);
|
|
197
|
-
// Assert: Should return valid port number or null
|
|
198
|
-
if (result !== null) {
|
|
199
|
-
expect(result).toBeGreaterThanOrEqual(9222);
|
|
200
|
-
expect(result).toBeLessThanOrEqual(9224);
|
|
201
|
-
}
|
|
202
|
-
else {
|
|
203
|
-
expect(result).toBe(null);
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
|
-
it('should handle empty port range', async () => {
|
|
207
|
-
// Arrange & Act: Find available port in impossible range
|
|
208
|
-
const result = await findAvailablePort(9999, 9998);
|
|
209
|
-
// Assert: Should return null for invalid range
|
|
210
|
-
expect(result).toBe(null);
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
describe('Circuit Breaker', () => {
|
|
214
|
-
it('should start in closed state', () => {
|
|
215
|
-
// Arrange & Act: Check initial circuit breaker state
|
|
216
|
-
const isOpen = isCircuitBreakerOpen();
|
|
217
|
-
// Assert: Should be closed initially
|
|
218
|
-
expect(isOpen).toBe(false);
|
|
219
|
-
});
|
|
220
|
-
it('should open circuit breaker after threshold failures', () => {
|
|
221
|
-
// Arrange: Clear circuit breaker state first
|
|
222
|
-
updateCircuitBreakerOnSuccess(); // Reset to closed state
|
|
223
|
-
// Act: Trigger multiple failures
|
|
224
|
-
for (let i = 0; i < 5; i++) {
|
|
225
|
-
updateCircuitBreakerOnFailure();
|
|
226
|
-
}
|
|
227
|
-
// Assert: Should be open after threshold
|
|
228
|
-
const isOpen = isCircuitBreakerOpen();
|
|
229
|
-
expect(isOpen).toBe(true);
|
|
230
|
-
});
|
|
231
|
-
it('should reset circuit breaker on success', () => {
|
|
232
|
-
// Arrange: Open circuit breaker first
|
|
233
|
-
for (let i = 0; i < 5; i++) {
|
|
234
|
-
updateCircuitBreakerOnFailure();
|
|
235
|
-
}
|
|
236
|
-
expect(isCircuitBreakerOpen()).toBe(true);
|
|
237
|
-
// Act: Record success
|
|
238
|
-
updateCircuitBreakerOnSuccess();
|
|
239
|
-
// Assert: Should be closed again
|
|
240
|
-
expect(isCircuitBreakerOpen()).toBe(false);
|
|
241
|
-
});
|
|
242
|
-
it('should enter half-open state after timeout', () => {
|
|
243
|
-
// Arrange: Open circuit breaker
|
|
244
|
-
for (let i = 0; i < 5; i++) {
|
|
245
|
-
updateCircuitBreakerOnFailure();
|
|
246
|
-
}
|
|
247
|
-
expect(isCircuitBreakerOpen()).toBe(true);
|
|
248
|
-
// Mock Date.now to simulate timeout passage
|
|
249
|
-
const originalNow = Date.now;
|
|
250
|
-
Date.now = vi.fn().mockReturnValue(originalNow() + 35000); // 35 seconds later
|
|
251
|
-
// Act: Check circuit breaker state
|
|
252
|
-
const isOpen = isCircuitBreakerOpen();
|
|
253
|
-
// Assert: Should transition to half-open (returns false)
|
|
254
|
-
expect(isOpen).toBe(false);
|
|
255
|
-
// Cleanup: Restore Date.now
|
|
256
|
-
Date.now = originalNow;
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
describe('Chrome Path Detection', () => {
|
|
260
|
-
it('should return environment variable path when available', () => {
|
|
261
|
-
// Arrange: Set environment variable and mock file exists
|
|
262
|
-
const chromePath = '/custom/chrome/path';
|
|
263
|
-
process.env.CHROME_PATH = chromePath;
|
|
264
|
-
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
265
|
-
// Act: Detect Chrome path
|
|
266
|
-
const result = detectChromePath();
|
|
267
|
-
// Assert: Should return environment path
|
|
268
|
-
expect(result).toBe(chromePath);
|
|
269
|
-
expect(fs.existsSync).toHaveBeenCalledWith(chromePath);
|
|
270
|
-
// Cleanup
|
|
271
|
-
delete process.env.CHROME_PATH;
|
|
272
|
-
});
|
|
273
|
-
it('should return null when Chrome is not found', () => {
|
|
274
|
-
// Arrange: Mock file system to return false for all paths
|
|
275
|
-
vi.mocked(fs.existsSync).mockReturnValue(false);
|
|
276
|
-
delete process.env.CHROME_PATH;
|
|
277
|
-
delete process.env.PUPPETEER_EXECUTABLE_PATH;
|
|
278
|
-
// Act: Detect Chrome path
|
|
279
|
-
const result = detectChromePath();
|
|
280
|
-
// Assert: Should return null
|
|
281
|
-
expect(result).toBe(null);
|
|
282
|
-
});
|
|
283
|
-
it('should detect Chrome on macOS platform', () => {
|
|
284
|
-
// Arrange: Mock platform and file system
|
|
285
|
-
Object.defineProperty(process, 'platform', { value: 'darwin' });
|
|
286
|
-
const expectedPath = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
|
|
287
|
-
vi.mocked(fs.existsSync).mockImplementation((path) => path === expectedPath);
|
|
288
|
-
delete process.env.CHROME_PATH;
|
|
289
|
-
// Act: Detect Chrome path
|
|
290
|
-
const result = detectChromePath();
|
|
291
|
-
// Assert: Should return macOS Chrome path
|
|
292
|
-
expect(result).toBe(expectedPath);
|
|
293
|
-
});
|
|
294
|
-
it('should detect Chrome on Linux platform', () => {
|
|
295
|
-
// Arrange: Mock platform and file system
|
|
296
|
-
Object.defineProperty(process, 'platform', { value: 'linux' });
|
|
297
|
-
const expectedPath = '/usr/bin/google-chrome';
|
|
298
|
-
vi.mocked(fs.existsSync).mockImplementation((path) => path === expectedPath);
|
|
299
|
-
delete process.env.CHROME_PATH;
|
|
300
|
-
// Act: Detect Chrome path
|
|
301
|
-
const result = detectChromePath();
|
|
302
|
-
// Assert: Should return Linux Chrome path
|
|
303
|
-
expect(result).toBe(expectedPath);
|
|
304
|
-
});
|
|
305
|
-
it('should return null for unsupported platform', () => {
|
|
306
|
-
// Arrange: Mock unsupported platform
|
|
307
|
-
Object.defineProperty(process, 'platform', { value: 'freebsd' });
|
|
308
|
-
delete process.env.CHROME_PATH;
|
|
309
|
-
// Act: Detect Chrome path
|
|
310
|
-
const result = detectChromePath();
|
|
311
|
-
// Assert: Should return null
|
|
312
|
-
expect(result).toBe(null);
|
|
313
|
-
});
|
|
314
|
-
});
|
|
315
|
-
describe('Session Validation', () => {
|
|
316
|
-
it('should return false when no browser instance exists', async () => {
|
|
317
|
-
// Arrange: No browser instance (default state)
|
|
318
|
-
// Act: Validate session
|
|
319
|
-
const result = await validateSession();
|
|
320
|
-
// Assert: Should return false
|
|
321
|
-
expect(result).toBe(false);
|
|
322
|
-
});
|
|
323
|
-
it('should return false when validation is already in progress', async () => {
|
|
324
|
-
// Arrange: We'll test this by calling validateSession twice quickly
|
|
325
|
-
// This requires mocking the internal state or testing the behavior indirectly
|
|
326
|
-
// For now, we'll test the basic case - this could be expanded with more complex mocking
|
|
327
|
-
const result = await validateSession();
|
|
328
|
-
// Assert: Should handle concurrent validation gracefully
|
|
329
|
-
expect(result).toBe(false);
|
|
330
|
-
});
|
|
331
|
-
});
|
|
332
|
-
describe('Auth Elements Finding', () => {
|
|
333
|
-
it('should find authentication elements in page content', async () => {
|
|
334
|
-
// Arrange: Mock page instance with evaluate method
|
|
335
|
-
const mockPage = {
|
|
336
|
-
evaluate: vi.fn().mockResolvedValue(['#login-button', '.signin-link'])
|
|
337
|
-
};
|
|
338
|
-
// Act: Find auth elements
|
|
339
|
-
const result = await findAuthElements(mockPage);
|
|
340
|
-
// Assert: Should return auth selectors
|
|
341
|
-
expect(result).toEqual(['#login-button', '.signin-link']);
|
|
342
|
-
expect(mockPage.evaluate).toHaveBeenCalledWith(expect.any(Function));
|
|
343
|
-
});
|
|
344
|
-
});
|
|
345
|
-
describe('Content Priority Configuration', () => {
|
|
346
|
-
it('should return current content priority config', () => {
|
|
347
|
-
// Arrange & Act: Get content priority config
|
|
348
|
-
const config = getContentPriorityConfig();
|
|
349
|
-
// Assert: Should return configuration object
|
|
350
|
-
expect(config).toBeDefined();
|
|
351
|
-
expect(config).toHaveProperty('prioritizeContent');
|
|
352
|
-
expect(config).toHaveProperty('autoSuggestGetContent');
|
|
353
|
-
});
|
|
354
|
-
it('should update content priority config', () => {
|
|
355
|
-
// Arrange: Get initial config
|
|
356
|
-
const initialConfig = getContentPriorityConfig();
|
|
357
|
-
const updates = { prioritizeContent: !initialConfig.prioritizeContent };
|
|
358
|
-
// Act: Update config
|
|
359
|
-
updateContentPriorityConfig(updates);
|
|
360
|
-
const updatedConfig = getContentPriorityConfig();
|
|
361
|
-
// Assert: Should reflect updates
|
|
362
|
-
expect(updatedConfig.prioritizeContent).toBe(updates.prioritizeContent);
|
|
363
|
-
expect(updatedConfig.autoSuggestGetContent).toBe(initialConfig.autoSuggestGetContent);
|
|
364
|
-
});
|
|
365
|
-
});
|
|
366
|
-
describe('Browser Instance Getters', () => {
|
|
367
|
-
it('should return browser instance', () => {
|
|
368
|
-
// Arrange & Act: Get browser instance
|
|
369
|
-
const browser = getBrowserInstance();
|
|
370
|
-
// Assert: Should return browser (null initially)
|
|
371
|
-
expect(browser).toBe(null);
|
|
372
|
-
});
|
|
373
|
-
it('should return page instance', () => {
|
|
374
|
-
// Arrange & Act: Get page instance
|
|
375
|
-
const page = getPageInstance();
|
|
376
|
-
// Assert: Should return page (null initially)
|
|
377
|
-
expect(page).toBe(null);
|
|
378
|
-
});
|
|
379
|
-
});
|
|
380
|
-
describe('Force Kill Chrome Processes', () => {
|
|
381
|
-
it('should execute without throwing errors', async () => {
|
|
382
|
-
// Arrange & Act: Force kill Chrome processes
|
|
383
|
-
// Act & Assert: Should not throw error regardless of platform
|
|
384
|
-
await expect(forceKillAllChromeProcesses()).resolves.toBeUndefined();
|
|
385
|
-
});
|
|
386
|
-
it('should handle different platforms', async () => {
|
|
387
|
-
// Arrange: Test with current platform
|
|
388
|
-
const originalPlatform = process.platform;
|
|
389
|
-
// Act: Execute force kill
|
|
390
|
-
await forceKillAllChromeProcesses();
|
|
391
|
-
// Assert: Should complete without error
|
|
392
|
-
expect(process.platform).toBe(originalPlatform);
|
|
393
|
-
});
|
|
394
|
-
});
|
|
395
|
-
});
|