cdp-tunnel 1.0.13 → 1.0.14
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/extension-new/background.js +6 -1
- package/extension-new/cdp/handler/special.js +47 -27
- package/extension-new/core/state.js +64 -6
- package/extension-new/core/websocket.js +124 -19
- package/package.json +9 -2
- package/server/proxy-server.js +45 -34
- package/.github/workflows/publish.yml +0 -92
- package/.github/workflows/release-assets.yml +0 -50
- package/PUBLISH.md +0 -65
- package/console-test.js +0 -52
- package/docs/README_CN.md +0 -204
- package/docs/config-page-screenshot.png +0 -0
- package/final-console-test.js +0 -105
- package/simple-tab-group-test.js +0 -56
- package/test-cdp-connection.js +0 -85
- package/test-cdp-groups.js +0 -71
- package/test-check-newtab.js +0 -144
- package/test-chrome-native.js +0 -140
- package/test-client-connected.js +0 -99
- package/test-compare-formats.js +0 -88
- package/test-context-features.js +0 -113
- package/test-create-tab.js +0 -113
- package/test-debug-broadcast.js +0 -52
- package/test-debug-targets.js +0 -127
- package/test-expose-newtab.js +0 -164
- package/test-expose-shared.js +0 -189
- package/test-final-logs.js +0 -110
- package/test-fresh-chromium.js +0 -153
- package/test-init-script.js +0 -128
- package/test-keepalive.js +0 -89
- package/test-launch-chromium.js +0 -140
- package/test-launch-vs-connect.js +0 -149
- package/test-listen-events.js +0 -102
- package/test-monitor.js +0 -83
- package/test-multiple-cdp-groups.js +0 -78
- package/test-native.js +0 -96
- package/test-page-connection.js +0 -74
- package/test-playwright-connection.js +0 -45
- package/test-playwright-groups.js +0 -47
- package/test-playwright-pages.js +0 -47
- package/test-playwright-sequence.js +0 -81
- package/test-proper-context.js +0 -129
- package/test-real-final.js +0 -251
- package/test-real-scenario-v2.js +0 -166
- package/test-real-scenario-v3.js +0 -231
- package/test-real-scenario.js +0 -104
- package/test-server-logs.js +0 -98
- package/test-session-id.js +0 -91
- package/test-simple-cdp-groups.js +0 -44
- package/test-simple-context.js +0 -137
- package/test-tab-group-simple.js +0 -58
- package/test-tab-grouping.js +0 -48
- package/test-three-pages.js +0 -192
- package/test-wait-for-page.js +0 -95
- package/test-with-logs.js +0 -118
- package/test-ws-groups.js +0 -59
- package/tests/e2e-auto-test.js +0 -304
- package/tests/iframe-test-page.html +0 -89
- package/tests/playwright-demo.js +0 -45
- package/tests/playwright-interactive.js +0 -261
- package/tests/playwright-multi-demo.js +0 -60
- package/tests/playwright-multi.js +0 -85
- package/tests/playwright-single.js +0 -41
- package/tests/screenshot-config.js +0 -35
- package/tests/test-client.js +0 -89
- package/tests/test-douyin-iframe.js +0 -171
- package/tests/test-iframe-debug.js +0 -204
- package/tests/test-multi-client.js +0 -129
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
const { chromium } = require('playwright');
|
|
2
|
-
|
|
3
|
-
const SERVER_URL = process.env.CDP_SERVER || 'http://localhost:9221';
|
|
4
|
-
const DOUYIN_URL = 'https://www.douyin.com/user/MS4wLjABAAAAnKeRN8QUgooS1pPRqOf_N_jnuztzUyocl0_vUndQFJs?modal_id=7635666432337351530';
|
|
5
|
-
|
|
6
|
-
async function sleep(ms) {
|
|
7
|
-
return new Promise(r => setTimeout(r, ms));
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
async function main() {
|
|
11
|
-
console.log('=== Douyin IFrame Disconnect Test ===');
|
|
12
|
-
console.log(`Server: ${SERVER_URL}\n`);
|
|
13
|
-
|
|
14
|
-
let browser;
|
|
15
|
-
try {
|
|
16
|
-
console.log('[1] Connecting to CDP tunnel...');
|
|
17
|
-
browser = await chromium.connectOverCDP(SERVER_URL, { timeout: 15000 });
|
|
18
|
-
console.log('[1] Connected!\n');
|
|
19
|
-
|
|
20
|
-
const context = browser.contexts()[0];
|
|
21
|
-
const page = context.pages()[0] || await context.newPage();
|
|
22
|
-
|
|
23
|
-
console.log('[2] Navigating to Douyin page...');
|
|
24
|
-
await page.goto(DOUYIN_URL, { waitUntil: 'domcontentloaded', timeout: 30000 });
|
|
25
|
-
console.log(`[2] Page loaded: ${page.url()}\n`);
|
|
26
|
-
|
|
27
|
-
await sleep(3000);
|
|
28
|
-
console.log('[3] Waiting for page to fully render...');
|
|
29
|
-
await sleep(2000);
|
|
30
|
-
|
|
31
|
-
// Check frame tree first
|
|
32
|
-
console.log('[4] Checking frame tree...');
|
|
33
|
-
try {
|
|
34
|
-
const cdpSession = await page.context().newCDPSession(page);
|
|
35
|
-
const frameTree = await cdpSession.send('Page.getFrameTree');
|
|
36
|
-
|
|
37
|
-
function printFrame(tree, indent) {
|
|
38
|
-
if (!tree || !tree.frame) return;
|
|
39
|
-
const pad = ' '.repeat(indent);
|
|
40
|
-
console.log(`${pad}Frame: id=${tree.frame.id} url=${(tree.frame.url || '').substring(0, 80)}`);
|
|
41
|
-
if (tree.childFrames) {
|
|
42
|
-
for (const child of tree.childFrames) printFrame(child, indent + 1);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
printFrame(frameTree.frameTree, 0);
|
|
46
|
-
await cdpSession.detach();
|
|
47
|
-
} catch (e) {
|
|
48
|
-
console.error('[4] Frame tree error:', e.message);
|
|
49
|
-
}
|
|
50
|
-
console.log('');
|
|
51
|
-
|
|
52
|
-
// Try to find and click the entry button
|
|
53
|
-
// The button has data-popupid attribute and contains an SVG
|
|
54
|
-
console.log('[5] Looking for the entry button...');
|
|
55
|
-
const selectors = [
|
|
56
|
-
'[data-popupid]',
|
|
57
|
-
'svg.wNbQukcA',
|
|
58
|
-
'.r68hW_1W',
|
|
59
|
-
];
|
|
60
|
-
|
|
61
|
-
let clicked = false;
|
|
62
|
-
for (const sel of selectors) {
|
|
63
|
-
try {
|
|
64
|
-
const el = page.locator(sel).first();
|
|
65
|
-
const count = await el.count();
|
|
66
|
-
if (count > 0) {
|
|
67
|
-
console.log(`[5] Found element with selector: ${sel}`);
|
|
68
|
-
await el.click({ timeout: 5000 });
|
|
69
|
-
console.log(`[5] Clicked!`);
|
|
70
|
-
clicked = true;
|
|
71
|
-
break;
|
|
72
|
-
}
|
|
73
|
-
} catch (e) {
|
|
74
|
-
console.log(`[5] Selector ${sel}: ${e.message.substring(0, 80)}`);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (!clicked) {
|
|
79
|
-
console.log('[5] Could not find entry button with known selectors, trying to list clickable elements...');
|
|
80
|
-
const buttons = await page.$$eval('[data-popupid]', els => els.map(e => ({
|
|
81
|
-
tag: e.tagName,
|
|
82
|
-
popupid: e.getAttribute('data-popupid'),
|
|
83
|
-
html: e.innerHTML.substring(0, 100)
|
|
84
|
-
})));
|
|
85
|
-
console.log('[5] Elements with data-popupid:', JSON.stringify(buttons, null, 2));
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
await sleep(3000);
|
|
89
|
-
|
|
90
|
-
// Check frame tree again after click
|
|
91
|
-
console.log('\n[6] Checking frame tree AFTER click...');
|
|
92
|
-
try {
|
|
93
|
-
const cdpSession = await page.context().newCDPSession(page);
|
|
94
|
-
const frameTree = await cdpSession.send('Page.getFrameTree');
|
|
95
|
-
printFrameTree(frameTree.frameTree, 0);
|
|
96
|
-
await cdpSession.detach();
|
|
97
|
-
} catch (e) {
|
|
98
|
-
console.error('[6] Frame tree error:', e.message);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// List all iframes on the page
|
|
102
|
-
console.log('\n[7] Listing all iframes...');
|
|
103
|
-
const iframes = await page.$$eval('iframe', els => els.map(e => ({
|
|
104
|
-
id: e.id,
|
|
105
|
-
src: e.src || e.getAttribute('srcdoc')?.substring(0, 50) || '(empty)',
|
|
106
|
-
className: e.className,
|
|
107
|
-
visible: e.offsetParent !== null
|
|
108
|
-
})));
|
|
109
|
-
console.log('[7] Iframes found:', JSON.stringify(iframes, null, 2));
|
|
110
|
-
|
|
111
|
-
// Try to find and interact with iframe input
|
|
112
|
-
console.log('\n[8] Trying to find input in iframes...');
|
|
113
|
-
const frames = page.frames();
|
|
114
|
-
console.log(`[8] Total frames: ${frames.length}`);
|
|
115
|
-
for (let i = 0; i < frames.length; i++) {
|
|
116
|
-
const frame = frames[i];
|
|
117
|
-
console.log(`[8] Frame ${i}: url=${frame.url().substring(0, 80)} name=${frame.name()}`);
|
|
118
|
-
|
|
119
|
-
if (frame === page.mainFrame()) continue;
|
|
120
|
-
|
|
121
|
-
try {
|
|
122
|
-
const inputs = await frame.locator('input, textarea').count();
|
|
123
|
-
console.log(`[8] Found ${inputs} input/textarea elements`);
|
|
124
|
-
|
|
125
|
-
if (inputs > 0) {
|
|
126
|
-
console.log(`[8] Attempting to click first input in frame ${i}...`);
|
|
127
|
-
const input = frame.locator('input, textarea').first();
|
|
128
|
-
await input.click({ timeout: 5000 });
|
|
129
|
-
console.log(`[8] CLICK SUCCEEDED - no disconnect!`);
|
|
130
|
-
|
|
131
|
-
await sleep(1000);
|
|
132
|
-
await input.fill('test input');
|
|
133
|
-
console.log(`[8] FILL SUCCEEDED`);
|
|
134
|
-
}
|
|
135
|
-
} catch (e) {
|
|
136
|
-
console.error(`[8] Frame ${i} error: ${e.message.substring(0, 120)}`);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
console.log('\n=== Test completed - Playwright did NOT disconnect ===');
|
|
141
|
-
|
|
142
|
-
} catch (e) {
|
|
143
|
-
console.error('\n!!! FATAL ERROR - Possible disconnect !!!');
|
|
144
|
-
console.error('Error:', e.message);
|
|
145
|
-
console.error('This might indicate Playwright disconnected');
|
|
146
|
-
} finally {
|
|
147
|
-
if (browser) {
|
|
148
|
-
try {
|
|
149
|
-
await browser.close();
|
|
150
|
-
console.log('\nBrowser closed normally');
|
|
151
|
-
} catch (e) {
|
|
152
|
-
console.error('\nBrowser close FAILED:', e.message);
|
|
153
|
-
console.error('This confirms Playwright disconnected');
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function printFrameTree(tree, indent) {
|
|
160
|
-
if (!tree || !tree.frame) return;
|
|
161
|
-
const pad = ' '.repeat(indent);
|
|
162
|
-
console.log(`${pad}Frame: id=${tree.frame.id} url=${(tree.frame.url || '').substring(0, 80)}`);
|
|
163
|
-
if (tree.childFrames) {
|
|
164
|
-
for (const child of tree.childFrames) printFrameTree(child, indent + 1);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
main().catch(e => {
|
|
169
|
-
console.error('UNHANDLED:', e);
|
|
170
|
-
process.exit(1);
|
|
171
|
-
});
|
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
const { chromium } = require('playwright');
|
|
2
|
-
|
|
3
|
-
const SERVER_URL = process.env.CDP_SERVER || 'http://localhost:9221';
|
|
4
|
-
const TEST_PAGE = `file://${__dirname}/iframe-test-page.html`;
|
|
5
|
-
|
|
6
|
-
async function sleep(ms) {
|
|
7
|
-
return new Promise(r => setTimeout(r, ms));
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
async function main() {
|
|
11
|
-
console.log('=== IFrame Debug Test ===');
|
|
12
|
-
console.log(`Server: ${SERVER_URL}`);
|
|
13
|
-
console.log(`Test page: ${TEST_PAGE}\n`);
|
|
14
|
-
|
|
15
|
-
let browser;
|
|
16
|
-
try {
|
|
17
|
-
console.log('[1] Connecting to CDP tunnel...');
|
|
18
|
-
browser = await chromium.connectOverCDP(SERVER_URL, {
|
|
19
|
-
timeout: 10000
|
|
20
|
-
});
|
|
21
|
-
console.log('[1] Connected!\n');
|
|
22
|
-
|
|
23
|
-
const context = browser.contexts()[0];
|
|
24
|
-
if (!context) {
|
|
25
|
-
throw new Error('No default context found');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const pages = context.pages();
|
|
29
|
-
let page;
|
|
30
|
-
if (pages.length > 0) {
|
|
31
|
-
page = pages[0];
|
|
32
|
-
console.log(`[2] Using existing page: ${page.url()}`);
|
|
33
|
-
} else {
|
|
34
|
-
page = await context.newPage();
|
|
35
|
-
console.log('[2] Created new page');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
console.log(`[2] Navigating to test page...`);
|
|
39
|
-
await page.goto(TEST_PAGE, { waitUntil: 'domcontentloaded', timeout: 15000 });
|
|
40
|
-
console.log(`[2] Page loaded: ${page.url()}\n`);
|
|
41
|
-
|
|
42
|
-
// --- Test 1: Main page input ---
|
|
43
|
-
console.log('[Test 1] Main page input...');
|
|
44
|
-
try {
|
|
45
|
-
await page.fill('#main-input-1', 'Hello from main');
|
|
46
|
-
const val1 = await page.inputValue('#main-input-1');
|
|
47
|
-
console.log(`[Test 1] OK - value: "${val1}"\n`);
|
|
48
|
-
} catch (e) {
|
|
49
|
-
console.error(`[Test 1] FAIL: ${e.message}\n`);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// --- Test 2: Main page button ---
|
|
53
|
-
console.log('[Test 2] Main page button click...');
|
|
54
|
-
try {
|
|
55
|
-
await page.click('#main-btn');
|
|
56
|
-
await sleep(500);
|
|
57
|
-
const status = await page.textContent('#status');
|
|
58
|
-
console.log(`[Test 2] OK - status: "${status}"\n`);
|
|
59
|
-
} catch (e) {
|
|
60
|
-
console.error(`[Test 2] FAIL: ${e.message}\n`);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// --- Test 3: Same-origin iframe ---
|
|
64
|
-
console.log('[Test 3] Same-origin iframe input...');
|
|
65
|
-
try {
|
|
66
|
-
const frame = page.frameLocator('#same-origin-iframe');
|
|
67
|
-
const input = frame.locator('#iframe-input-1');
|
|
68
|
-
await input.fill('Hello from iframe');
|
|
69
|
-
const val = await input.inputValue();
|
|
70
|
-
console.log(`[Test 3] OK - value: "${val}"\n`);
|
|
71
|
-
} catch (e) {
|
|
72
|
-
console.error(`[Test 3] FAIL: ${e.message}\n`);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// --- Test 4: Same-origin iframe button ---
|
|
76
|
-
console.log('[Test 4] Same-origin iframe button click...');
|
|
77
|
-
try {
|
|
78
|
-
const frame = page.frameLocator('#same-origin-iframe');
|
|
79
|
-
await frame.locator('#iframe-btn').click();
|
|
80
|
-
await sleep(500);
|
|
81
|
-
const result = await frame.locator('#iframe-result').textContent();
|
|
82
|
-
console.log(`[Test 4] OK - result: "${result}"\n`);
|
|
83
|
-
} catch (e) {
|
|
84
|
-
console.error(`[Test 4] FAIL: ${e.message}\n`);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// --- Test 5: Cross-origin iframe (Wikipedia) ---
|
|
88
|
-
console.log('[Test 5] Cross-origin iframe (Wikipedia)...');
|
|
89
|
-
try {
|
|
90
|
-
const frame = page.frameLocator('#cross-origin-iframe');
|
|
91
|
-
// Just try to access the frame - this will likely fail
|
|
92
|
-
const title = await frame.locator('h1').first().textContent({ timeout: 5000 });
|
|
93
|
-
console.log(`[Test 5] OK - title: "${title}"\n`);
|
|
94
|
-
} catch (e) {
|
|
95
|
-
console.error(`[Test 5] FAIL (expected): ${e.message}\n`);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// --- Test 6: Nested iframe ---
|
|
99
|
-
console.log('[Test 6] Nested iframe (level 1 input)...');
|
|
100
|
-
try {
|
|
101
|
-
const frame = page.frameLocator('#nested-iframe');
|
|
102
|
-
await frame.locator('#l1-input').fill('Level 1');
|
|
103
|
-
const val = await frame.locator('#l1-input').inputValue();
|
|
104
|
-
console.log(`[Test 6] OK - value: "${val}"\n`);
|
|
105
|
-
} catch (e) {
|
|
106
|
-
console.error(`[Test 6] FAIL: ${e.message}\n`);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// --- Test 7: Nested iframe level 2 ---
|
|
110
|
-
console.log('[Test 7] Nested iframe (level 2 input)...');
|
|
111
|
-
try {
|
|
112
|
-
const frame = page.frameLocator('#nested-iframe').frameLocator('#nested-inner');
|
|
113
|
-
await frame.locator('#l2-input').fill('Level 2');
|
|
114
|
-
const val = await frame.locator('#l2-input').inputValue();
|
|
115
|
-
console.log(`[Test 7] OK - value: "${val}"\n`);
|
|
116
|
-
} catch (e) {
|
|
117
|
-
console.error(`[Test 7] FAIL: ${e.message}\n`);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// --- Test 8: Dynamic iframe ---
|
|
121
|
-
console.log('[Test 8] Dynamic iframe...');
|
|
122
|
-
try {
|
|
123
|
-
await page.click('#create-iframe-btn');
|
|
124
|
-
await sleep(1000);
|
|
125
|
-
const frame = page.frameLocator('#dynamic-iframe');
|
|
126
|
-
await frame.locator('#dyn-input').fill('Dynamic!');
|
|
127
|
-
const val = await frame.locator('#dyn-input').inputValue();
|
|
128
|
-
console.log(`[Test 8] OK - value: "${val}"\n`);
|
|
129
|
-
} catch (e) {
|
|
130
|
-
console.error(`[Test 8] FAIL: ${e.message}\n`);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// --- Test 9: Page.getFrameTree via CDP directly ---
|
|
134
|
-
console.log('[Test 9] CDP Page.getFrameTree...');
|
|
135
|
-
try {
|
|
136
|
-
const cdpSession = await page.context().newCDPSession(page);
|
|
137
|
-
const frameTree = await cdpSession.send('Page.getFrameTree');
|
|
138
|
-
console.log(`[Test 9] Frame tree:`);
|
|
139
|
-
printFrameTree(frameTree.frameTree, 0);
|
|
140
|
-
await cdpSession.detach();
|
|
141
|
-
console.log('');
|
|
142
|
-
} catch (e) {
|
|
143
|
-
console.error(`[Test 9] FAIL: ${e.message}\n`);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// --- Test 10: Target.setAutoAttach + Target.attachedToTarget ---
|
|
147
|
-
console.log('[Test 10] CDP Target.setAutoAttach + iframe sessions...');
|
|
148
|
-
try {
|
|
149
|
-
const cdpSession = await page.context().newCDPSession(page);
|
|
150
|
-
|
|
151
|
-
let attachedEventCount = 0;
|
|
152
|
-
cdpSession.on('Target.attachedToTarget', (event) => {
|
|
153
|
-
attachedEventCount++;
|
|
154
|
-
console.log(`[Test 10] Target.attachedToTarget event #${attachedEventCount}:`);
|
|
155
|
-
console.log(` sessionId: ${event.sessionId}`);
|
|
156
|
-
console.log(` targetInfo.type: ${event.targetInfo?.type}`);
|
|
157
|
-
console.log(` targetInfo.targetId: ${event.targetInfo?.targetId}`);
|
|
158
|
-
console.log(` targetInfo.url: ${event.targetInfo?.url}`);
|
|
159
|
-
console.log(` waitingForDebugger: ${event.waitingForDebugger}`);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
cdpSession.on('Target.targetCreated', (event) => {
|
|
163
|
-
console.log(`[Test 10] Target.targetCreated: type=${event.targetInfo?.type} targetId=${event.targetInfo?.targetId}`);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
await cdpSession.send('Target.setAutoAttach', {
|
|
167
|
-
autoAttach: true,
|
|
168
|
-
waitForDebuggerOnStart: false,
|
|
169
|
-
flatten: true
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
await sleep(3000);
|
|
173
|
-
|
|
174
|
-
console.log(`[Test 10] Total attachedToTarget events: ${attachedEventCount}`);
|
|
175
|
-
await cdpSession.detach();
|
|
176
|
-
console.log('');
|
|
177
|
-
} catch (e) {
|
|
178
|
-
console.error(`[Test 10] FAIL: ${e.message}\n`);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
console.log('=== All tests completed ===');
|
|
182
|
-
|
|
183
|
-
} catch (e) {
|
|
184
|
-
console.error('Fatal error:', e);
|
|
185
|
-
} finally {
|
|
186
|
-
if (browser) {
|
|
187
|
-
await browser.close().catch(() => {});
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function printFrameTree(tree, indent) {
|
|
193
|
-
if (!tree || !tree.frame) return;
|
|
194
|
-
const pad = ' '.repeat(indent);
|
|
195
|
-
const frame = tree.frame;
|
|
196
|
-
console.log(`${pad}Frame: id=${frame.id} url=${frame.url?.substring(0, 60)}`);
|
|
197
|
-
if (tree.childFrames) {
|
|
198
|
-
for (const child of tree.childFrames) {
|
|
199
|
-
printFrameTree(child, indent + 1);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
main().catch(console.error);
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
const WebSocket = require('ws');
|
|
2
|
-
|
|
3
|
-
const SERVER_URL = process.argv[2] || 'ws://localhost:9221';
|
|
4
|
-
const NUM_CLIENTS = parseInt(process.argv[3]) || 2;
|
|
5
|
-
|
|
6
|
-
console.log(`Starting ${NUM_CLIENTS} test clients connecting to ${SERVER_URL}...\n`);
|
|
7
|
-
|
|
8
|
-
const clients = [];
|
|
9
|
-
|
|
10
|
-
for (let i = 0; i < NUM_CLIENTS; i++) {
|
|
11
|
-
const clientId = `test-client-${i + 1}`;
|
|
12
|
-
const client = createClient(clientId);
|
|
13
|
-
clients.push(client);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function createClient(clientId) {
|
|
17
|
-
console.log(`[${clientId}] Connecting...`);
|
|
18
|
-
|
|
19
|
-
const ws = new WebSocket(SERVER_URL);
|
|
20
|
-
let commandId = 1;
|
|
21
|
-
|
|
22
|
-
ws.on('open', () => {
|
|
23
|
-
console.log(`[${clientId}] Connected!`);
|
|
24
|
-
|
|
25
|
-
ws.send(JSON.stringify({
|
|
26
|
-
type: 'identify',
|
|
27
|
-
clientId: clientId
|
|
28
|
-
}));
|
|
29
|
-
|
|
30
|
-
ws.send(JSON.stringify({
|
|
31
|
-
id: commandId++,
|
|
32
|
-
method: 'Target.setDiscoverTargets',
|
|
33
|
-
params: { discover: true }
|
|
34
|
-
}));
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
ws.on('message', (data) => {
|
|
38
|
-
try {
|
|
39
|
-
const msg = JSON.parse(data.toString());
|
|
40
|
-
|
|
41
|
-
if (msg.method === 'Target.targetCreated') {
|
|
42
|
-
console.log(`[${clientId}] Target created: ${msg.params.targetInfo?.url || msg.params.targetInfo?.type}`);
|
|
43
|
-
} else if (msg.method === 'Target.attachedToTarget') {
|
|
44
|
-
console.log(`[${clientId}] Attached to target: sessionId=${msg.params.sessionId?.substring(0, 8)}...`);
|
|
45
|
-
} else if (msg.method === 'Target.targetDestroyed') {
|
|
46
|
-
console.log(`[${clientId}] Target destroyed`);
|
|
47
|
-
} else if (msg.id) {
|
|
48
|
-
if (msg.result?.targetInfos) {
|
|
49
|
-
console.log(`[${clientId}] Got ${msg.result.targetInfos.length} targets`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
} catch (e) {}
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
ws.on('close', () => {
|
|
56
|
-
console.log(`[${clientId}] Disconnected`);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
ws.on('error', (err) => {
|
|
60
|
-
console.error(`[${clientId}] Error:`, err.message);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
ws,
|
|
65
|
-
clientId,
|
|
66
|
-
send: (method, params) => {
|
|
67
|
-
ws.send(JSON.stringify({ id: commandId++, method, params }));
|
|
68
|
-
},
|
|
69
|
-
close: () => ws.close()
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function interactiveTest() {
|
|
74
|
-
const readline = require('readline');
|
|
75
|
-
const rl = readline.createInterface({
|
|
76
|
-
input: process.stdin,
|
|
77
|
-
output: process.stdout
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
console.log('\n=== Interactive Test Commands ===');
|
|
81
|
-
console.log(' n <client_idx> <url> - Client #n creates new page');
|
|
82
|
-
console.log(' c <client_idx> - Client #n closes current page');
|
|
83
|
-
console.log(' s <client_idx> - Client #n scrolls page');
|
|
84
|
-
console.log(' l - List all clients');
|
|
85
|
-
console.log(' q - Quit');
|
|
86
|
-
console.log('================================\n');
|
|
87
|
-
|
|
88
|
-
rl.on('line', (input) => {
|
|
89
|
-
const parts = input.trim().split(' ');
|
|
90
|
-
const cmd = parts[0];
|
|
91
|
-
|
|
92
|
-
if (cmd === 'q') {
|
|
93
|
-
console.log('Closing all clients...');
|
|
94
|
-
clients.forEach(c => c.close());
|
|
95
|
-
setTimeout(() => process.exit(0), 500);
|
|
96
|
-
} else if (cmd === 'l') {
|
|
97
|
-
console.log(`Active clients: ${clients.length}`);
|
|
98
|
-
clients.forEach((c, i) => {
|
|
99
|
-
console.log(` #${i + 1}: ${c.clientId} - ${c.ws.readyState === WebSocket.OPEN ? 'connected' : 'disconnected'}`);
|
|
100
|
-
});
|
|
101
|
-
} else if (cmd === 'n' && parts.length >= 3) {
|
|
102
|
-
const idx = parseInt(parts[1]) - 1;
|
|
103
|
-
const url = parts[2] || 'https://www.baidu.com';
|
|
104
|
-
if (clients[idx]) {
|
|
105
|
-
console.log(`Client #${idx + 1} creating page: ${url}`);
|
|
106
|
-
clients[idx].send('Target.createTarget', { url });
|
|
107
|
-
}
|
|
108
|
-
} else if (cmd === 's' && parts.length >= 2) {
|
|
109
|
-
const idx = parseInt(parts[1]) - 1;
|
|
110
|
-
if (clients[idx]) {
|
|
111
|
-
console.log(`Client #${idx + 1} scrolling...`);
|
|
112
|
-
clients[idx].send('Runtime.evaluate', {
|
|
113
|
-
expression: 'window.scrollBy(0, 100)'
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
setTimeout(interactiveTest, 1000);
|
|
121
|
-
|
|
122
|
-
process.on('SIGINT', () => {
|
|
123
|
-
console.log('\nClosing all clients...');
|
|
124
|
-
clients.forEach(c => c.close());
|
|
125
|
-
process.exit(0);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
console.log('Usage: node test-multi-client.js [server_url] [num_clients]');
|
|
129
|
-
console.log('Example: node test-multi-client.js ws://localhost:9221 2\n');
|