real-browser-mcp-server 1.0.10 → 1.1.2
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/README.md +19 -2
- package/lib/cjs/index.js +41 -2
- package/lib/esm/index.mjs +41 -2
- package/package.json +1 -1
- package/src/mcp/handlers.js +25 -9
- package/test/cjs/test.js +125 -1
- package/test/esm/test.mjs +125 -1
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ npx patchright install chromium
|
|
|
35
35
|
|
|
36
36
|
* **Undetected Browser Engine**: Powered by **Patchright Chromium**, bypassing modern fingerprinting checks (does not expose automation indicators or Webdriver/BiDi flags).
|
|
37
37
|
* **Integrated Ad & Tracker Blocker**: Utilizes `@ghostery/adblocker-playwright` with asynchronous pre-compiled filter caching to `adblocker.bin`, blocking ads and speed-bumps completely offline.
|
|
38
|
-
* **Human-like Interactions**: Integrates **ghost-cursor-patchright** (Bézier curves) to transparently simulate human mouse movements, velocity, and natural hover-before-click behaviors.
|
|
38
|
+
* **Human-like Interactions**: Integrates **ghost-cursor-patchright** (Bézier curves) to transparently simulate human mouse movements, velocity, and natural hover-before-click behaviors. Features **Physics-based Smooth Scrolling** (`page.realScroll`) utilizing real mouse-wheel events and Cubic Ease-Out deceleration to perfectly mimic manual trackpad/mouse flicks, bypassing advanced behavioral detectors.
|
|
39
39
|
* **Turnstile Auto-Solver**: Seamlessly detects and bypasses Cloudflare Turnstile widgets.
|
|
40
40
|
* **Anti-Race Condition Guards**: Robust state-guards ensure popup blockers, shims, and adblockers attach exactly once per page, preventing context destruction.
|
|
41
41
|
|
|
@@ -158,7 +158,7 @@ The server exposes 22 highly optimized tools categorized into functional units:
|
|
|
158
158
|
| `click` | Human-like click using AI healing, ghost cursor, and iframe support. | `selector` (string), `hoverFirst` (boolean) |
|
|
159
159
|
| `type` | Type text with human speed variation, smart clearing, and iframe support. | `selector` (string), `text` (string) |
|
|
160
160
|
| `solve_captcha` | Auto-solve CAPTCHAs (Turnstile, reCAPTCHA, hCaptcha, OCR). | `selector` (string) |
|
|
161
|
-
| `random_scroll` | Simulated human scrolling with natural patterns and lazy-load triggers. | `direction` (string), `amount` (number) |
|
|
161
|
+
| `random_scroll` | Simulated human scrolling with natural patterns and lazy-load triggers. | `direction` (string), `amount` (number), `smooth` (boolean) |
|
|
162
162
|
| `press_key` | Press keyboard keys with modifier key support (Ctrl/Shift/Alt). | `key` (string), `modifiers` (array) |
|
|
163
163
|
| `execute_js` | Run custom asynchronous/synchronous JavaScript inside a page or iframe. | `code` (string), `iframeIndex` (number) |
|
|
164
164
|
|
|
@@ -213,6 +213,11 @@ Both the CommonJS and ES Module test suites execute and pass successfully under
|
|
|
213
213
|
| **CommonJS (`cjs_test`)** | Fingerprint JS Bot Detector | ✅ Passed |
|
|
214
214
|
| **CommonJS (`cjs_test`)** | Recaptcha V3 Score | ✅ Passed |
|
|
215
215
|
| **CommonJS (`cjs_test`)** | Pixelscan Fingerprint Check | ✅ Passed |
|
|
216
|
+
| **CommonJS (`cjs_test`)** | Human-like Move & Click | ✅ Passed |
|
|
217
|
+
| **CommonJS (`cjs_test`)** | Human-like Typing | ✅ Passed |
|
|
218
|
+
| **CommonJS (`cjs_test`)** | Human-like Scrolling | ✅ Passed |
|
|
219
|
+
| **CommonJS (`cjs_test`)** | Form Automation Demonstration | ✅ Passed |
|
|
220
|
+
| **CommonJS (`cjs_test`)** | Content Strategy Demonstration | ✅ Passed |
|
|
216
221
|
| **ES Module (`esm_test`)** | DrissionPage Detector | ✅ Passed |
|
|
217
222
|
| **ES Module (`esm_test`)** | Sannysoft WebDriver Detector | ✅ Passed |
|
|
218
223
|
| **ES Module (`esm_test`)** | Cloudflare WAF | ✅ Passed |
|
|
@@ -220,6 +225,11 @@ Both the CommonJS and ES Module test suites execute and pass successfully under
|
|
|
220
225
|
| **ES Module (`esm_test`)** | Fingerprint JS Bot Detector | ✅ Passed |
|
|
221
226
|
| **ES Module (`esm_test`)** | Recaptcha V3 Score | ✅ Passed |
|
|
222
227
|
| **ES Module (`esm_test`)** | Pixelscan Fingerprint Check | ✅ Passed |
|
|
228
|
+
| **ES Module (`esm_test`)** | Human-like Move & Click | ✅ Passed |
|
|
229
|
+
| **ES Module (`esm_test`)** | Human-like Typing | ✅ Passed |
|
|
230
|
+
| **ES Module (`esm_test`)** | Human-like Scrolling | ✅ Passed |
|
|
231
|
+
| **ES Module (`esm_test`)** | Form Automation Demonstration | ✅ Passed |
|
|
232
|
+
| **ES Module (`esm_test`)** | Content Strategy Demonstration | ✅ Passed |
|
|
223
233
|
|
|
224
234
|
---
|
|
225
235
|
|
|
@@ -244,6 +254,9 @@ const { connect } = require('real-browser-mcp-server');
|
|
|
244
254
|
// Real mouse movement and click
|
|
245
255
|
await page.realClick('#my-button');
|
|
246
256
|
|
|
257
|
+
// Real human-like smooth scrolling (60FPS Cubic Ease-Out physics)
|
|
258
|
+
await page.realScroll(400); // scrolls down 400px smoothly
|
|
259
|
+
|
|
247
260
|
await browser.close();
|
|
248
261
|
})();
|
|
249
262
|
```
|
|
@@ -259,6 +272,10 @@ const { browser, page } = await connect({
|
|
|
259
272
|
|
|
260
273
|
await page.goto('https://example.com');
|
|
261
274
|
await page.realClick('#my-button');
|
|
275
|
+
|
|
276
|
+
// Real human-like smooth scrolling (60FPS Cubic Ease-Out physics)
|
|
277
|
+
await page.realScroll(400); // scrolls down 400px smoothly
|
|
278
|
+
|
|
262
279
|
await browser.close();
|
|
263
280
|
```
|
|
264
281
|
|
package/lib/cjs/index.js
CHANGED
|
@@ -66,8 +66,23 @@ function loadEnvFile() {
|
|
|
66
66
|
loadEnvFile();
|
|
67
67
|
|
|
68
68
|
function getDefaultHeadless() {
|
|
69
|
-
const envHeadless =
|
|
70
|
-
|
|
69
|
+
const envHeadless = process.env.HEADLESS;
|
|
70
|
+
if (envHeadless !== undefined && envHeadless !== null && envHeadless !== '') {
|
|
71
|
+
const value = envHeadless.toLowerCase().trim();
|
|
72
|
+
return value === 'true' || value === '1' || value === 'yes';
|
|
73
|
+
}
|
|
74
|
+
// Auto-detect CI environments
|
|
75
|
+
if (process.env.CI || process.env.GITHUB_ACTIONS || process.env.TRAVIS || process.env.CIRCLECI) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
// Auto-detect headless Linux environments without X11 or Wayland
|
|
79
|
+
if (process.platform === 'linux') {
|
|
80
|
+
const hasDisplay = process.env.DISPLAY || process.env.WAYLAND_DISPLAY;
|
|
81
|
+
if (!hasDisplay) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
71
86
|
}
|
|
72
87
|
|
|
73
88
|
function setupRealPage(browser, page) {
|
|
@@ -85,6 +100,29 @@ function setupRealPage(browser, page) {
|
|
|
85
100
|
});
|
|
86
101
|
}
|
|
87
102
|
|
|
103
|
+
// Human-like smooth scrolling with 60FPS Cubic Ease-Out physics
|
|
104
|
+
page.realScroll = async (deltaY, duration = 600) => {
|
|
105
|
+
try {
|
|
106
|
+
const stepDelay = 15; // ~60 FPS
|
|
107
|
+
const steps = Math.max(10, Math.floor(duration / stepDelay));
|
|
108
|
+
const easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
|
|
109
|
+
let currentScroll = 0;
|
|
110
|
+
for (let i = 1; i <= steps; i++) {
|
|
111
|
+
const t = i / steps;
|
|
112
|
+
const targetScroll = deltaY * easeOutCubic(t);
|
|
113
|
+
const diff = targetScroll - currentScroll;
|
|
114
|
+
await page.mouse.wheel(0, diff);
|
|
115
|
+
currentScroll = targetScroll;
|
|
116
|
+
await new Promise(r => setTimeout(r, stepDelay));
|
|
117
|
+
}
|
|
118
|
+
} catch (e) {
|
|
119
|
+
// Fallback to native window scroll in case of wheel errors
|
|
120
|
+
try {
|
|
121
|
+
await page.evaluate((y) => window.scrollBy({ top: y, behavior: 'smooth' }), deltaY);
|
|
122
|
+
} catch (_) {}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
88
126
|
// Ghost Cursor integration - Bézier curve human-like mouse movement
|
|
89
127
|
try {
|
|
90
128
|
const cursor = createCursor(page);
|
|
@@ -123,6 +161,7 @@ function setupRealPage(browser, page) {
|
|
|
123
161
|
}
|
|
124
162
|
|
|
125
163
|
return page;
|
|
164
|
+
|
|
126
165
|
}
|
|
127
166
|
|
|
128
167
|
function getBraveExecutablePath() {
|
package/lib/esm/index.mjs
CHANGED
|
@@ -71,8 +71,23 @@ function loadEnvFile() {
|
|
|
71
71
|
loadEnvFile();
|
|
72
72
|
|
|
73
73
|
function getDefaultHeadless() {
|
|
74
|
-
const envHeadless =
|
|
75
|
-
|
|
74
|
+
const envHeadless = process.env.HEADLESS;
|
|
75
|
+
if (envHeadless !== undefined && envHeadless !== null && envHeadless !== '') {
|
|
76
|
+
const value = envHeadless.toLowerCase().trim();
|
|
77
|
+
return value === 'true' || value === '1' || value === 'yes';
|
|
78
|
+
}
|
|
79
|
+
// Auto-detect CI environments
|
|
80
|
+
if (process.env.CI || process.env.GITHUB_ACTIONS || process.env.TRAVIS || process.env.CIRCLECI) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
// Auto-detect headless Linux environments without X11 or Wayland
|
|
84
|
+
if (process.platform === 'linux') {
|
|
85
|
+
const hasDisplay = process.env.DISPLAY || process.env.WAYLAND_DISPLAY;
|
|
86
|
+
if (!hasDisplay) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
76
91
|
}
|
|
77
92
|
|
|
78
93
|
function setupRealPage(browser, page) {
|
|
@@ -90,6 +105,29 @@ function setupRealPage(browser, page) {
|
|
|
90
105
|
});
|
|
91
106
|
}
|
|
92
107
|
|
|
108
|
+
// Human-like smooth scrolling with 60FPS Cubic Ease-Out physics
|
|
109
|
+
page.realScroll = async (deltaY, duration = 600) => {
|
|
110
|
+
try {
|
|
111
|
+
const stepDelay = 15; // ~60 FPS
|
|
112
|
+
const steps = Math.max(10, Math.floor(duration / stepDelay));
|
|
113
|
+
const easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
|
|
114
|
+
let currentScroll = 0;
|
|
115
|
+
for (let i = 1; i <= steps; i++) {
|
|
116
|
+
const t = i / steps;
|
|
117
|
+
const targetScroll = deltaY * easeOutCubic(t);
|
|
118
|
+
const diff = targetScroll - currentScroll;
|
|
119
|
+
await page.mouse.wheel(0, diff);
|
|
120
|
+
currentScroll = targetScroll;
|
|
121
|
+
await new Promise(r => setTimeout(r, stepDelay));
|
|
122
|
+
}
|
|
123
|
+
} catch (e) {
|
|
124
|
+
// Fallback to native window scroll in case of wheel errors
|
|
125
|
+
try {
|
|
126
|
+
await page.evaluate((y) => window.scrollBy({ top: y, behavior: 'smooth' }), deltaY);
|
|
127
|
+
} catch (_) {}
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
93
131
|
// Ghost Cursor integration - Bézier curve human-like mouse movement
|
|
94
132
|
try {
|
|
95
133
|
const cursor = createCursor(page);
|
|
@@ -128,6 +166,7 @@ function setupRealPage(browser, page) {
|
|
|
128
166
|
}
|
|
129
167
|
|
|
130
168
|
return page;
|
|
169
|
+
|
|
131
170
|
}
|
|
132
171
|
|
|
133
172
|
function getBraveExecutablePath() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "real-browser-mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "MCP Server for Real Browser - Patchright (undetected Playwright fork) with Stealth Mode, Ad Blocker, and Turnstile Auto-Solver for undetectable web automation.",
|
|
5
5
|
"main": "lib/cjs/index.js",
|
|
6
6
|
"module": "lib/esm/index.mjs",
|
package/src/mcp/handlers.js
CHANGED
|
@@ -68,13 +68,25 @@ function notifyProgress(toolName, status, message, data = {}) {
|
|
|
68
68
|
function getHeadlessFromEnv() {
|
|
69
69
|
const envHeadless = process.env.HEADLESS;
|
|
70
70
|
|
|
71
|
-
if (envHeadless
|
|
72
|
-
|
|
71
|
+
if (envHeadless !== undefined && envHeadless !== null && envHeadless !== '') {
|
|
72
|
+
const value = envHeadless.toLowerCase().trim();
|
|
73
|
+
return value === 'true' || value === '1' || value === 'yes';
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
// Auto-detect CI environments
|
|
77
|
+
if (process.env.CI || process.env.GITHUB_ACTIONS || process.env.TRAVIS || process.env.CIRCLECI) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Auto-detect headless Linux environments without X11 or Wayland
|
|
82
|
+
if (process.platform === 'linux') {
|
|
83
|
+
const hasDisplay = process.env.DISPLAY || process.env.WAYLAND_DISPLAY;
|
|
84
|
+
if (!hasDisplay) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return false;
|
|
78
90
|
}
|
|
79
91
|
|
|
80
92
|
/**
|
|
@@ -2049,10 +2061,14 @@ const handlers = {
|
|
|
2049
2061
|
|
|
2050
2062
|
notifyProgress('random_scroll', 'started', `Scrolling ${scrollDirection} ${scrollAmount}px`);
|
|
2051
2063
|
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
}
|
|
2064
|
+
const y = scrollDirection === 'down' ? scrollAmount : -scrollAmount;
|
|
2065
|
+
if (smooth && page.realScroll) {
|
|
2066
|
+
await page.realScroll(y, 600);
|
|
2067
|
+
} else {
|
|
2068
|
+
await page.evaluate(({ y, smooth }) => {
|
|
2069
|
+
window.scrollBy({ top: y, behavior: smooth ? 'smooth' : 'auto' });
|
|
2070
|
+
}, { y, smooth });
|
|
2071
|
+
}
|
|
2056
2072
|
|
|
2057
2073
|
notifyProgress('random_scroll', 'completed', `Scrolled ${scrollDirection} ${scrollAmount}px`, { direction: scrollDirection, amount: scrollAmount });
|
|
2058
2074
|
|
package/test/cjs/test.js
CHANGED
|
@@ -4,7 +4,7 @@ const { connect } = require('../../lib/cjs/index.js');
|
|
|
4
4
|
|
|
5
5
|
const realBrowserOption = {
|
|
6
6
|
turnstile: true,
|
|
7
|
-
headless:
|
|
7
|
+
headless: false,
|
|
8
8
|
customConfig: {}
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -30,6 +30,126 @@ test.after(async () => {
|
|
|
30
30
|
}
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
+
test('Human-like Move & Click', async () => {
|
|
34
|
+
await page.goto("https://www.google.com", { timeout: 40000 });
|
|
35
|
+
const selector = 'textarea[name="q"], input[name="q"]';
|
|
36
|
+
await page.realCursor.move(selector);
|
|
37
|
+
await page.realClick(selector);
|
|
38
|
+
assert.ok(true);
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('Human-like Typing', async () => {
|
|
42
|
+
await page.goto("https://www.google.com", { timeout: 40000 });
|
|
43
|
+
const selector = 'textarea[name="q"], input[name="q"]';
|
|
44
|
+
await page.realCursor.move(selector);
|
|
45
|
+
await page.realClick(selector);
|
|
46
|
+
await page.type(selector, 'Real Browser MCP Server', { delay: 150 });
|
|
47
|
+
const val = await page.inputValue(selector);
|
|
48
|
+
assert.strictEqual(val, 'Real Browser MCP Server');
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('Human-like Scrolling', async () => {
|
|
52
|
+
await page.goto("https://www.google.com/search?q=Real+Browser+MCP+Server", { timeout: 40000, waitUntil: 'domcontentloaded' });
|
|
53
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
54
|
+
|
|
55
|
+
console.log('📜 Scrolling down smoothly and fast (400px)...');
|
|
56
|
+
await page.realScroll(400, 500);
|
|
57
|
+
await new Promise(r => setTimeout(r, 600));
|
|
58
|
+
|
|
59
|
+
console.log('📜 Scrolling down smoothly and fast (300px)...');
|
|
60
|
+
await page.realScroll(300, 400);
|
|
61
|
+
await new Promise(r => setTimeout(r, 600));
|
|
62
|
+
|
|
63
|
+
console.log('📜 Scrolling up smoothly and fast (-500px)...');
|
|
64
|
+
await page.realScroll(-500, 600);
|
|
65
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
66
|
+
|
|
67
|
+
assert.ok(true);
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('Form Automation Demonstration', async () => {
|
|
71
|
+
console.log('\n🎬 DEMO: Form Automation');
|
|
72
|
+
try {
|
|
73
|
+
await page.goto('https://httpbin.org/forms/post', { timeout: 30000 });
|
|
74
|
+
console.log('\n4️⃣ Filling out form...');
|
|
75
|
+
|
|
76
|
+
// 1. Customer Name
|
|
77
|
+
await page.type('input[name="custname"]', 'John Doe', { delay: 100 });
|
|
78
|
+
console.log('✅ Customer Name filled');
|
|
79
|
+
|
|
80
|
+
// 2. Telephone
|
|
81
|
+
await page.type('input[name="custtel"]', '+1-555-0199', { delay: 100 });
|
|
82
|
+
console.log('✅ Telephone filled');
|
|
83
|
+
|
|
84
|
+
// 3. Email address
|
|
85
|
+
await page.type('input[name="custemail"]', 'john.doe@example.com', { delay: 100 });
|
|
86
|
+
console.log('✅ Email field filled');
|
|
87
|
+
|
|
88
|
+
// 4. Pizza Size (Radio Button)
|
|
89
|
+
await page.realClick('input[value="medium"]');
|
|
90
|
+
console.log('✅ Pizza Size selected (Medium)');
|
|
91
|
+
|
|
92
|
+
// 5. Pizza Toppings (Checkboxes)
|
|
93
|
+
await page.realClick('input[value="bacon"]');
|
|
94
|
+
await page.realClick('input[value="onion"]');
|
|
95
|
+
console.log('✅ Toppings selected (Bacon, Onion)');
|
|
96
|
+
|
|
97
|
+
// 6. Preferred Delivery Time
|
|
98
|
+
await page.type('input[name="delivery"]', '13:00', { delay: 100 });
|
|
99
|
+
console.log('✅ Delivery time filled');
|
|
100
|
+
|
|
101
|
+
// 7. Delivery Instructions (Comments)
|
|
102
|
+
await page.type('textarea[name="comments"]', 'Leave at the front door, please.', { delay: 100 });
|
|
103
|
+
console.log('✅ Delivery instructions filled');
|
|
104
|
+
|
|
105
|
+
// Wait 2 seconds for visual demonstration
|
|
106
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
107
|
+
|
|
108
|
+
// 8. Submit Order
|
|
109
|
+
await page.realClick('form button');
|
|
110
|
+
console.log('✅ Form submitted successfully');
|
|
111
|
+
|
|
112
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
113
|
+
console.log('\n🎉 FORM AUTOMATION COMPLETE!');
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error('❌ Form automation test failed:', error);
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
test('Content Strategy Demonstration', async () => {
|
|
121
|
+
console.log('\n🎬 DEMO: Content Analysis & Token Management');
|
|
122
|
+
console.log('👀 Watch browser analyze content from different websites');
|
|
123
|
+
try {
|
|
124
|
+
const testSites = [
|
|
125
|
+
{ url: 'https://httpbin.org/html', description: 'Simple HTML page' },
|
|
126
|
+
{ url: 'https://example.com', description: 'Minimal content page' }
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
for (const [index, site] of testSites.entries()) {
|
|
130
|
+
console.log(`\n${index + 2}️⃣ Testing ${site.description}: ${site.url}`);
|
|
131
|
+
await page.goto(site.url, { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
132
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
133
|
+
|
|
134
|
+
console.log(` 📄 Getting HTML content...`);
|
|
135
|
+
const htmlContent = await page.content();
|
|
136
|
+
console.log(` ✅ HTML analyzed: ${htmlContent.length} characters`);
|
|
137
|
+
|
|
138
|
+
console.log(` 📝 Getting text content...`);
|
|
139
|
+
const textContent = await page.evaluate(() => document.body.innerText);
|
|
140
|
+
console.log(` ✅ Text analyzed: ${textContent.length} characters`);
|
|
141
|
+
|
|
142
|
+
assert.ok(htmlContent.length > 0);
|
|
143
|
+
assert.ok(textContent.length > 0);
|
|
144
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
145
|
+
}
|
|
146
|
+
console.log('\n🎉 CONTENT ANALYSIS COMPLETE!');
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error('❌ Content strategy test failed:', error);
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
})
|
|
152
|
+
|
|
33
153
|
test('DrissionPage Detector', async () => {
|
|
34
154
|
await page.goto("https://web.archive.org/web/20240913054632/https://drissionpage.pages.dev/", { timeout: 60000 });
|
|
35
155
|
await page.realClick("#detector")
|
|
@@ -210,3 +330,7 @@ test('Pixelscan Fingerprint Check', async () => {
|
|
|
210
330
|
assert.strictEqual(result, true, "Pixelscan Fingerprint Check failed! Browser fingerprint is inconsistent or masking was detected.");
|
|
211
331
|
})
|
|
212
332
|
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
|
package/test/esm/test.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { connect } from '../../lib/esm/index.mjs';
|
|
|
4
4
|
|
|
5
5
|
const realBrowserOption = {
|
|
6
6
|
turnstile: true,
|
|
7
|
-
headless:
|
|
7
|
+
headless: false,
|
|
8
8
|
customConfig: {}
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -30,6 +30,126 @@ test.after(async () => {
|
|
|
30
30
|
}
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
+
test('Human-like Move & Click', async () => {
|
|
34
|
+
await page.goto("https://www.google.com", { timeout: 40000 });
|
|
35
|
+
const selector = 'textarea[name="q"], input[name="q"]';
|
|
36
|
+
await page.realCursor.move(selector);
|
|
37
|
+
await page.realClick(selector);
|
|
38
|
+
assert.ok(true);
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('Human-like Typing', async () => {
|
|
42
|
+
await page.goto("https://www.google.com", { timeout: 40000 });
|
|
43
|
+
const selector = 'textarea[name="q"], input[name="q"]';
|
|
44
|
+
await page.realCursor.move(selector);
|
|
45
|
+
await page.realClick(selector);
|
|
46
|
+
await page.type(selector, 'Real Browser MCP Server', { delay: 150 });
|
|
47
|
+
const val = await page.inputValue(selector);
|
|
48
|
+
assert.strictEqual(val, 'Real Browser MCP Server');
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('Human-like Scrolling', async () => {
|
|
52
|
+
await page.goto("https://www.google.com/search?q=Real+Browser+MCP+Server", { timeout: 40000, waitUntil: 'domcontentloaded' });
|
|
53
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
54
|
+
|
|
55
|
+
console.log('📜 Scrolling down smoothly and fast (400px)...');
|
|
56
|
+
await page.realScroll(400, 500);
|
|
57
|
+
await new Promise(r => setTimeout(r, 600));
|
|
58
|
+
|
|
59
|
+
console.log('📜 Scrolling down smoothly and fast (300px)...');
|
|
60
|
+
await page.realScroll(300, 400);
|
|
61
|
+
await new Promise(r => setTimeout(r, 600));
|
|
62
|
+
|
|
63
|
+
console.log('📜 Scrolling up smoothly and fast (-500px)...');
|
|
64
|
+
await page.realScroll(-500, 600);
|
|
65
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
66
|
+
|
|
67
|
+
assert.ok(true);
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('Form Automation Demonstration', async () => {
|
|
71
|
+
console.log('\n🎬 DEMO: Form Automation');
|
|
72
|
+
try {
|
|
73
|
+
await page.goto('https://httpbin.org/forms/post', { timeout: 30000 });
|
|
74
|
+
console.log('\n4️⃣ Filling out form...');
|
|
75
|
+
|
|
76
|
+
// 1. Customer Name
|
|
77
|
+
await page.type('input[name="custname"]', 'John Doe', { delay: 100 });
|
|
78
|
+
console.log('✅ Customer Name filled');
|
|
79
|
+
|
|
80
|
+
// 2. Telephone
|
|
81
|
+
await page.type('input[name="custtel"]', '+1-555-0199', { delay: 100 });
|
|
82
|
+
console.log('✅ Telephone filled');
|
|
83
|
+
|
|
84
|
+
// 3. Email address
|
|
85
|
+
await page.type('input[name="custemail"]', 'john.doe@example.com', { delay: 100 });
|
|
86
|
+
console.log('✅ Email field filled');
|
|
87
|
+
|
|
88
|
+
// 4. Pizza Size (Radio Button)
|
|
89
|
+
await page.realClick('input[value="medium"]');
|
|
90
|
+
console.log('✅ Pizza Size selected (Medium)');
|
|
91
|
+
|
|
92
|
+
// 5. Pizza Toppings (Checkboxes)
|
|
93
|
+
await page.realClick('input[value="bacon"]');
|
|
94
|
+
await page.realClick('input[value="onion"]');
|
|
95
|
+
console.log('✅ Toppings selected (Bacon, Onion)');
|
|
96
|
+
|
|
97
|
+
// 6. Preferred Delivery Time
|
|
98
|
+
await page.type('input[name="delivery"]', '13:00', { delay: 100 });
|
|
99
|
+
console.log('✅ Delivery time filled');
|
|
100
|
+
|
|
101
|
+
// 7. Delivery Instructions (Comments)
|
|
102
|
+
await page.type('textarea[name="comments"]', 'Leave at the front door, please.', { delay: 100 });
|
|
103
|
+
console.log('✅ Delivery instructions filled');
|
|
104
|
+
|
|
105
|
+
// Wait 2 seconds for visual demonstration
|
|
106
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
107
|
+
|
|
108
|
+
// 8. Submit Order
|
|
109
|
+
await page.realClick('form button');
|
|
110
|
+
console.log('✅ Form submitted successfully');
|
|
111
|
+
|
|
112
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
113
|
+
console.log('\n🎉 FORM AUTOMATION COMPLETE!');
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error('❌ Form automation test failed:', error);
|
|
116
|
+
throw error;
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
test('Content Strategy Demonstration', async () => {
|
|
121
|
+
console.log('\n🎬 DEMO: Content Analysis & Token Management');
|
|
122
|
+
console.log('👀 Watch browser analyze content from different websites');
|
|
123
|
+
try {
|
|
124
|
+
const testSites = [
|
|
125
|
+
{ url: 'https://httpbin.org/html', description: 'Simple HTML page' },
|
|
126
|
+
{ url: 'https://example.com', description: 'Minimal content page' }
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
for (const [index, site] of testSites.entries()) {
|
|
130
|
+
console.log(`\n${index + 2}️⃣ Testing ${site.description}: ${site.url}`);
|
|
131
|
+
await page.goto(site.url, { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
132
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
133
|
+
|
|
134
|
+
console.log(` 📄 Getting HTML content...`);
|
|
135
|
+
const htmlContent = await page.content();
|
|
136
|
+
console.log(` ✅ HTML analyzed: ${htmlContent.length} characters`);
|
|
137
|
+
|
|
138
|
+
console.log(` 📝 Getting text content...`);
|
|
139
|
+
const textContent = await page.evaluate(() => document.body.innerText);
|
|
140
|
+
console.log(` ✅ Text analyzed: ${textContent.length} characters`);
|
|
141
|
+
|
|
142
|
+
assert.ok(htmlContent.length > 0);
|
|
143
|
+
assert.ok(textContent.length > 0);
|
|
144
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
145
|
+
}
|
|
146
|
+
console.log('\n🎉 CONTENT ANALYSIS COMPLETE!');
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error('❌ Content strategy test failed:', error);
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
})
|
|
152
|
+
|
|
33
153
|
test('DrissionPage Detector', async () => {
|
|
34
154
|
await page.goto("https://web.archive.org/web/20240913054632/https://drissionpage.pages.dev/", { timeout: 70000 });
|
|
35
155
|
await page.realClick("#detector")
|
|
@@ -173,3 +293,7 @@ test('Pixelscan Fingerprint Check', async () => {
|
|
|
173
293
|
|
|
174
294
|
assert.strictEqual(result, true, "Pixelscan Fingerprint Check failed! Browser fingerprint is inconsistent or masking was detected.");
|
|
175
295
|
})
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
|