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.
Files changed (68) hide show
  1. package/extension-new/background.js +6 -1
  2. package/extension-new/cdp/handler/special.js +47 -27
  3. package/extension-new/core/state.js +64 -6
  4. package/extension-new/core/websocket.js +124 -19
  5. package/package.json +9 -2
  6. package/server/proxy-server.js +45 -34
  7. package/.github/workflows/publish.yml +0 -92
  8. package/.github/workflows/release-assets.yml +0 -50
  9. package/PUBLISH.md +0 -65
  10. package/console-test.js +0 -52
  11. package/docs/README_CN.md +0 -204
  12. package/docs/config-page-screenshot.png +0 -0
  13. package/final-console-test.js +0 -105
  14. package/simple-tab-group-test.js +0 -56
  15. package/test-cdp-connection.js +0 -85
  16. package/test-cdp-groups.js +0 -71
  17. package/test-check-newtab.js +0 -144
  18. package/test-chrome-native.js +0 -140
  19. package/test-client-connected.js +0 -99
  20. package/test-compare-formats.js +0 -88
  21. package/test-context-features.js +0 -113
  22. package/test-create-tab.js +0 -113
  23. package/test-debug-broadcast.js +0 -52
  24. package/test-debug-targets.js +0 -127
  25. package/test-expose-newtab.js +0 -164
  26. package/test-expose-shared.js +0 -189
  27. package/test-final-logs.js +0 -110
  28. package/test-fresh-chromium.js +0 -153
  29. package/test-init-script.js +0 -128
  30. package/test-keepalive.js +0 -89
  31. package/test-launch-chromium.js +0 -140
  32. package/test-launch-vs-connect.js +0 -149
  33. package/test-listen-events.js +0 -102
  34. package/test-monitor.js +0 -83
  35. package/test-multiple-cdp-groups.js +0 -78
  36. package/test-native.js +0 -96
  37. package/test-page-connection.js +0 -74
  38. package/test-playwright-connection.js +0 -45
  39. package/test-playwright-groups.js +0 -47
  40. package/test-playwright-pages.js +0 -47
  41. package/test-playwright-sequence.js +0 -81
  42. package/test-proper-context.js +0 -129
  43. package/test-real-final.js +0 -251
  44. package/test-real-scenario-v2.js +0 -166
  45. package/test-real-scenario-v3.js +0 -231
  46. package/test-real-scenario.js +0 -104
  47. package/test-server-logs.js +0 -98
  48. package/test-session-id.js +0 -91
  49. package/test-simple-cdp-groups.js +0 -44
  50. package/test-simple-context.js +0 -137
  51. package/test-tab-group-simple.js +0 -58
  52. package/test-tab-grouping.js +0 -48
  53. package/test-three-pages.js +0 -192
  54. package/test-wait-for-page.js +0 -95
  55. package/test-with-logs.js +0 -118
  56. package/test-ws-groups.js +0 -59
  57. package/tests/e2e-auto-test.js +0 -304
  58. package/tests/iframe-test-page.html +0 -89
  59. package/tests/playwright-demo.js +0 -45
  60. package/tests/playwright-interactive.js +0 -261
  61. package/tests/playwright-multi-demo.js +0 -60
  62. package/tests/playwright-multi.js +0 -85
  63. package/tests/playwright-single.js +0 -41
  64. package/tests/screenshot-config.js +0 -35
  65. package/tests/test-client.js +0 -89
  66. package/tests/test-douyin-iframe.js +0 -171
  67. package/tests/test-iframe-debug.js +0 -204
  68. package/tests/test-multi-client.js +0 -129
package/test-ws-groups.js DELETED
@@ -1,59 +0,0 @@
1
- const WebSocket = require('ws');
2
-
3
- console.log('=== 测试CDP连接的标签分组功能 ===\n');
4
-
5
- // 连接到CDP服务器
6
- console.log('连接到CDP服务器...');
7
- const ws = new WebSocket('ws://localhost:9221/client');
8
-
9
- ws.on('open', function() {
10
- console.log('CDP连接成功');
11
-
12
- // 创建第一个标签页
13
- console.log('创建第一个标签页...');
14
- ws.send(JSON.stringify({
15
- id: 1,
16
- method: 'Target.createTarget',
17
- params: {
18
- url: 'https://www.baidu.com/'
19
- }
20
- }));
21
-
22
- // 等待5秒后创建第二个标签页
23
- setTimeout(function() {
24
- console.log('创建第二个标签页...');
25
- ws.send(JSON.stringify({
26
- id: 2,
27
- method: 'Target.createTarget',
28
- params: {
29
- url: 'https://www.google.com/'
30
- }
31
- }));
32
-
33
- // 等待5秒后关闭连接
34
- setTimeout(function() {
35
- console.log('\n=== 测试完成 ===');
36
- console.log('请检查Chrome浏览器中的标签组:');
37
- console.log('1. 应该创建了两个新的标签页');
38
- console.log('2. 这两个标签页应该被添加到一个名为"CDP-{客户端ID}"的标签组中');
39
- ws.close();
40
- }, 5000);
41
- }, 5000);
42
- });
43
-
44
- ws.on('message', function(data) {
45
- try {
46
- const message = JSON.parse(data);
47
- console.log('收到消息:', JSON.stringify(message, null, 2));
48
- } catch (e) {
49
- console.error('解析消息失败:', e);
50
- }
51
- });
52
-
53
- ws.on('error', function(error) {
54
- console.error('WebSocket错误:', error);
55
- });
56
-
57
- ws.on('close', function() {
58
- console.log('WebSocket连接已关闭');
59
- });
@@ -1,304 +0,0 @@
1
- const { chromium } = require('playwright');
2
- const { spawn } = require('child_process');
3
- const http = require('http');
4
- const path = require('path');
5
- const fs = require('fs');
6
-
7
- const CHROMIUM = '/Applications/Chromium.app/Contents/MacOS/Chromium';
8
- const CHROME = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
9
- const EXTENSION_PATH = path.join(__dirname, '..', 'extension-new');
10
- const SERVER_SCRIPT = path.join(__dirname, '..', 'server', 'proxy-server.js');
11
- const USER_DATA_DIR = '/tmp/cdp-tunnel-e2e-test';
12
- const PROXY_PORT = 19221;
13
- const CDP_URL = `http://localhost:${PROXY_PORT}`;
14
- const TEST_EXT_DIR = '/tmp/cdp-tunnel-e2e-extension';
15
-
16
- const CHROME_PATH = fs.existsSync(CHROMIUM) ? CHROMIUM : fs.existsSync(CHROME) ? CHROME : null;
17
-
18
- if (!CHROME_PATH) {
19
- console.error('Chrome/Chromium not found');
20
- process.exit(1);
21
- }
22
-
23
- async function sleep(ms) {
24
- return new Promise(r => setTimeout(r, ms));
25
- }
26
-
27
- function httpGet(url) {
28
- return new Promise((resolve, reject) => {
29
- http.get(url, (res) => {
30
- let data = '';
31
- res.on('data', (chunk) => data += chunk);
32
- res.on('end', () => resolve(data));
33
- }).on('error', reject);
34
- });
35
- }
36
-
37
- async function waitForServer(port, maxWait = 10000) {
38
- const start = Date.now();
39
- while (Date.now() - start < maxWait) {
40
- try {
41
- await httpGet(`http://localhost:${port}/json/version`);
42
- return true;
43
- } catch {
44
- await sleep(500);
45
- }
46
- }
47
- return false;
48
- }
49
-
50
- async function waitForExtension(maxWait = 15000) {
51
- const start = Date.now();
52
- while (Date.now() - start < maxWait) {
53
- try {
54
- const data = await httpGet(`http://localhost:${PROXY_PORT}/json/version`);
55
- const info = JSON.parse(data);
56
- return true;
57
- } catch {}
58
- await sleep(500);
59
- }
60
- return false;
61
- }
62
-
63
- async function runTest() {
64
- let serverProc = null;
65
- let chromeProc = null;
66
- let browser = null;
67
- let passed = 0;
68
- let failed = 0;
69
-
70
- console.log('='.repeat(60));
71
- console.log(' CDP Tunnel E2E Automated Test');
72
- console.log('='.repeat(60));
73
- console.log(` Chrome: ${CHROME_PATH}`);
74
- console.log(` Extension: ${EXTENSION_PATH}`);
75
- console.log(` Proxy: ${CDP_URL}`);
76
- console.log('='.repeat(60));
77
-
78
- try {
79
- // 1. Cleanup
80
- console.log('\n[Setup] Cleaning up...');
81
- if (fs.existsSync(USER_DATA_DIR)) {
82
- fs.rmSync(USER_DATA_DIR, { recursive: true });
83
- }
84
- if (fs.existsSync(TEST_EXT_DIR)) {
85
- fs.rmSync(TEST_EXT_DIR, { recursive: true });
86
- }
87
-
88
- // Kill any existing process on port
89
- const { execSync } = require('child_process');
90
- try { execSync(`lsof -ti :${PROXY_PORT} | xargs kill -9 2>/dev/null`, { stdio: 'ignore' }); } catch {}
91
- await sleep(500);
92
-
93
- // 2. Copy extension and patch WS port
94
- console.log('[Setup] Preparing test extension (port ' + PROXY_PORT + ')...');
95
- execSync(`cp -r "${EXTENSION_PATH}" "${TEST_EXT_DIR}"`, { stdio: 'ignore' });
96
- const configPath = path.join(TEST_EXT_DIR, 'utils', 'config.js');
97
- let configContent = fs.readFileSync(configPath, 'utf8');
98
- configContent = configContent.replace(
99
- /WS_URL:\s*'ws:\/\/localhost:\d+\/plugin'/,
100
- `WS_URL: 'ws://localhost:${PROXY_PORT}/plugin'`
101
- );
102
- fs.writeFileSync(configPath, configContent);
103
-
104
- // 2. Start proxy server
105
- console.log('[Setup] Starting proxy server...');
106
- serverProc = spawn('node', [SERVER_SCRIPT], {
107
- detached: false,
108
- stdio: ['ignore', 'pipe', 'pipe'],
109
- env: { ...process.env, PORT: String(PROXY_PORT) }
110
- });
111
- serverProc.stdout.on('data', (d) => {
112
- const msg = d.toString().trim();
113
- if (msg.includes('PLUGIN') || msg.includes('CLIENT') || msg.includes('IFRAME')) {
114
- console.log(` [Server] ${msg.substring(0, 120)}`);
115
- }
116
- });
117
- serverProc.stderr.on('data', () => {});
118
-
119
- if (!(await waitForServer(PROXY_PORT))) {
120
- throw new Error('Proxy server failed to start');
121
- }
122
- console.log('[Setup] Proxy server started');
123
-
124
- // 3. Launch Chrome with extension
125
- console.log('[Setup] Launching Chrome with extension...');
126
- chromeProc = spawn(CHROME_PATH, [
127
- `--user-data-dir=${USER_DATA_DIR}`,
128
- '--no-first-run',
129
- '--no-default-browser-check',
130
- '--disable-background-timer-throttling',
131
- '--disable-backgrounding-occluded-windows',
132
- '--disable-renderer-backgrounding',
133
- `--load-extension=${TEST_EXT_DIR}`,
134
- '--enable-features=AutomationControlled',
135
- 'about:blank'
136
- ], {
137
- detached: false,
138
- stdio: 'ignore'
139
- });
140
-
141
- console.log('[Setup] Waiting for extension to connect...');
142
- await sleep(5000);
143
-
144
- // 4. Connect Playwright
145
- console.log('[Setup] Connecting Playwright...');
146
- browser = await chromium.connectOverCDP(CDP_URL, { timeout: 15000 });
147
- console.log('[Setup] Connected!\n');
148
-
149
- const context = browser.contexts()[0];
150
- const pages = context.pages();
151
- let page = pages.find(p => p.url() === 'about:blank') || pages[0];
152
- if (!page) page = await context.newPage();
153
-
154
- // === TESTS ===
155
-
156
- // Test 1: Basic page navigation
157
- console.log('[Test 1] Basic page navigation...');
158
- try {
159
- await page.goto('https://example.com', { waitUntil: 'domcontentloaded', timeout: 10000 });
160
- const title = await page.title();
161
- console.log(`[Test 1] PASS - title: "${title}"`);
162
- passed++;
163
- } catch (e) {
164
- console.error(`[Test 1] FAIL - ${e.message}`);
165
- failed++;
166
- }
167
-
168
- // Test 2: Fill input on main page
169
- console.log('\n[Test 2] Fill input on main page...');
170
- try {
171
- await page.goto(`file://${path.join(__dirname, 'iframe-test-page.html')}`, { waitUntil: 'domcontentloaded', timeout: 10000 });
172
- await page.fill('#main-input-1', 'E2E test');
173
- const val = await page.inputValue('#main-input-1');
174
- if (val === 'E2E test') {
175
- console.log(`[Test 2] PASS - value: "${val}"`);
176
- passed++;
177
- } else {
178
- console.error(`[Test 2] FAIL - expected "E2E test", got "${val}"`);
179
- failed++;
180
- }
181
- } catch (e) {
182
- console.error(`[Test 2] FAIL - ${e.message}`);
183
- failed++;
184
- }
185
-
186
- // Test 3: Same-origin iframe fill
187
- console.log('\n[Test 3] Same-origin iframe fill...');
188
- try {
189
- const frame = page.frameLocator('#same-origin-iframe');
190
- await frame.locator('#iframe-input-1').fill('iframe works', { timeout: 10000 });
191
- const val = await frame.locator('#iframe-input-1').inputValue({ timeout: 5000 });
192
- if (val === 'iframe works') {
193
- console.log(`[Test 3] PASS - value: "${val}"`);
194
- passed++;
195
- } else {
196
- console.error(`[Test 3] FAIL - expected "iframe works", got "${val}"`);
197
- failed++;
198
- }
199
- } catch (e) {
200
- console.error(`[Test 3] FAIL - ${e.message.substring(0, 100)}`);
201
- failed++;
202
- }
203
-
204
- // Test 4: Create new page
205
- console.log('\n[Test 4] Create new page...');
206
- try {
207
- const newPage = await context.newPage();
208
- await newPage.goto('https://example.com', { waitUntil: 'domcontentloaded', timeout: 10000 });
209
- const title = await newPage.title();
210
- await newPage.close();
211
- console.log(`[Test 4] PASS - new page title: "${title}"`);
212
- passed++;
213
- } catch (e) {
214
- console.error(`[Test 4] FAIL - ${e.message}`);
215
- failed++;
216
- }
217
-
218
- // Test 5: Page.getFrameTree
219
- console.log('\n[Test 5] CDP Page.getFrameTree...');
220
- try {
221
- const cdpSession = await page.context().newCDPSession(page);
222
- const frameTree = await cdpSession.send('Page.getFrameTree');
223
- const mainFrame = frameTree.frameTree.frame;
224
- const childCount = frameTree.frameTree.childFrames?.length || 0;
225
- await cdpSession.detach();
226
- console.log(`[Test 5] PASS - main frame: ${mainFrame.id} children: ${childCount}`);
227
- passed++;
228
- } catch (e) {
229
- console.error(`[Test 5] FAIL - ${e.message}`);
230
- failed++;
231
- }
232
-
233
- // Test 6: Target.setAutoAttach event count
234
- console.log('\n[Test 6] Target.setAutoAttach events...');
235
- try {
236
- const cdpSession = await page.context().newCDPSession(page);
237
- let eventCount = 0;
238
- cdpSession.on('Target.attachedToTarget', () => eventCount++);
239
- await cdpSession.send('Target.setAutoAttach', {
240
- autoAttach: true,
241
- waitForDebuggerOnStart: false,
242
- flatten: true
243
- });
244
- await sleep(3000);
245
- await cdpSession.detach();
246
- console.log(`[Test 6] PASS - attachedToTarget events: ${eventCount}`);
247
- passed++;
248
- } catch (e) {
249
- console.error(`[Test 6] FAIL - ${e.message}`);
250
- failed++;
251
- }
252
-
253
- // Test 7: Douyin page (custom protocol blocking) - no disconnect
254
- console.log('\n[Test 7] Douyin custom protocol (no disconnect)...');
255
- try {
256
- await page.goto('https://www.douyin.com', { waitUntil: 'domcontentloaded', timeout: 20000 });
257
- await sleep(3000);
258
- // If we get here without disconnect, the test passes
259
- const url = page.url();
260
- if (url.includes('douyin')) {
261
- console.log(`[Test 7] PASS - page alive at ${url.substring(0, 50)}`);
262
- passed++;
263
- } else {
264
- console.error(`[Test 7] FAIL - unexpected URL: ${url}`);
265
- failed++;
266
- }
267
- } catch (e) {
268
- console.error(`[Test 7] FAIL (possible disconnect) - ${e.message.substring(0, 120)}`);
269
- failed++;
270
- }
271
-
272
- } catch (e) {
273
- console.error(`\n!!! FATAL: ${e.message}`);
274
- failed++;
275
- } finally {
276
- // Cleanup
277
- console.log('\n[Cleanup] Tearing down...');
278
- if (browser) {
279
- try { await browser.close(); } catch {}
280
- }
281
- if (chromeProc) {
282
- try { chromeProc.kill('SIGKILL'); } catch {}
283
- }
284
- if (serverProc) {
285
- try { serverProc.kill('SIGKILL'); } catch {}
286
- }
287
- // Kill any leftover processes on our port
288
- const { execSync } = require('child_process');
289
- try { execSync(`lsof -ti :${PROXY_PORT} | xargs kill -9 2>/dev/null`, { stdio: 'ignore' }); } catch {}
290
- try {
291
- if (fs.existsSync(USER_DATA_DIR)) {
292
- fs.rmSync(USER_DATA_DIR, { recursive: true });
293
- }
294
- } catch {}
295
-
296
- console.log('\n' + '='.repeat(60));
297
- console.log(` Results: ${passed} passed, ${failed} failed`);
298
- console.log('='.repeat(60));
299
-
300
- process.exit(failed > 0 ? 1 : 0);
301
- }
302
- }
303
-
304
- runTest();
@@ -1,89 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="zh-CN">
3
- <head>
4
- <meta charset="UTF-8">
5
- <title>IFrame Test Page</title>
6
- <style>
7
- body { font-family: sans-serif; padding: 20px; }
8
- .section { margin: 20px 0; padding: 15px; border: 1px solid #ccc; border-radius: 8px; }
9
- h2 { margin-top: 0; color: #333; }
10
- iframe { width: 100%; height: 200px; border: 2px solid #666; margin: 10px 0; }
11
- input { padding: 8px; font-size: 14px; margin: 5px; }
12
- button { padding: 8px 16px; font-size: 14px; cursor: pointer; }
13
- #status { margin-top: 10px; padding: 10px; background: #f0f0f0; border-radius: 4px; }
14
- </style>
15
- </head>
16
- <body>
17
- <h1>CDP Tunnel - IFrame Test Page</h1>
18
-
19
- <div class="section">
20
- <h2>Main Page Inputs</h2>
21
- <input id="main-input-1" type="text" placeholder="Main input 1" />
22
- <input id="main-input-2" type="text" placeholder="Main input 2" />
23
- <button id="main-btn" onclick="document.getElementById('status').textContent='Main button clicked at ' + new Date().toISOString()">Main Button</button>
24
- <div id="status"></div>
25
- </div>
26
-
27
- <div class="section">
28
- <h2>Same-Origin IFrame</h2>
29
- <iframe id="same-origin-iframe" srcdoc="
30
- <html>
31
- <body style='padding:10px; font-family:sans-serif;'>
32
- <h3>Same-Origin IFrame Content</h3>
33
- <input id='iframe-input-1' type='text' placeholder='Same-origin input 1' />
34
- <input id='iframe-input-2' type='text' placeholder='Same-origin input 2' />
35
- <button id='iframe-btn' onclick=\"document.getElementById('iframe-result').textContent='Iframe button clicked!'\">
36
- Iframe Button
37
- </button>
38
- <div id='iframe-result'></div>
39
- </body>
40
- </html>
41
- "></iframe>
42
- </div>
43
-
44
- <div class="section">
45
- <h2>Cross-Origin IFrame (Wikipedia)</h2>
46
- <iframe id="cross-origin-iframe" src="https://en.wikipedia.org/wiki/Main_Page" sandbox="allow-scripts allow-same-origin allow-forms"></iframe>
47
- </div>
48
-
49
- <div class="section">
50
- <h2>Nested IFrame (same-origin, 2 levels)</h2>
51
- <iframe id="nested-iframe" srcdoc="
52
- <html>
53
- <body style='padding:10px; font-family:sans-serif;'>
54
- <h3>Level 1 IFrame</h3>
55
- <input id='l1-input' type='text' placeholder='Level 1 input' />
56
- <iframe id='nested-inner' srcdoc=\"
57
- <html>
58
- <body style='padding:10px; font-family:sans-serif;'>
59
- <h4>Level 2 Nested IFrame</h4>
60
- <input id='l2-input' type='text' placeholder='Level 2 input' />
61
- </body>
62
- </html>
63
- \" width='100%' height='120' style='border:1px solid #999;'></iframe>
64
- </body>
65
- </html>
66
- "></iframe>
67
- </div>
68
-
69
- <div class="section">
70
- <h2>Dynamic IFrame (created by JS)</h2>
71
- <button id="create-iframe-btn" onclick="createDynamicIframe()">Create Dynamic IFrame</button>
72
- <div id="dynamic-iframe-container"></div>
73
- <script>
74
- function createDynamicIframe() {
75
- const container = document.getElementById('dynamic-iframe-container');
76
- const iframe = document.createElement('iframe');
77
- iframe.id = 'dynamic-iframe';
78
- iframe.width = '100%';
79
- iframe.height = '150';
80
- iframe.style.border = '2px solid red';
81
- iframe.style.marginTop = '10px';
82
- iframe.srcdoc = '<html><body style="padding:10px; font-family:sans-serif;"><h3>Dynamic IFrame</h3><input id="dyn-input" type="text" placeholder="Dynamic iframe input" /></body></html>';
83
- container.appendChild(iframe);
84
- document.getElementById('status').textContent = 'Dynamic iframe created';
85
- }
86
- </script>
87
- </div>
88
- </body>
89
- </html>
@@ -1,45 +0,0 @@
1
- const { chromium } = require('playwright');
2
-
3
- async function main() {
4
- console.log('[Playwright] Connecting to http://localhost:9221...');
5
-
6
- const browser = await chromium.connectOverCDP('http://localhost:9221');
7
- console.log('[Playwright] Connected!');
8
-
9
- const context = browser.contexts()[0];
10
- const pages = context?.pages() || [];
11
- console.log('[Playwright] Found', pages.length, 'page(s)');
12
-
13
- console.log('\n>>> 请现在打开配置页面查看状态:');
14
- console.log('>>> chrome-extension://bchclccgjmihieacfmaelkpfjlghhoph/config-page-preview.html');
15
- console.log('>>> 等待 10 秒...\n');
16
-
17
- await new Promise(r => setTimeout(r, 10000));
18
-
19
- console.log('[Playwright] Creating new page...');
20
- const page = await context.newPage();
21
- await page.goto('https://www.baidu.com');
22
- console.log('[Playwright] New page URL:', page.url());
23
-
24
- console.log('\n>>> 新页面已创建,请查看配置页面是否显示');
25
- console.log('>>> 等待 10 秒...\n');
26
-
27
- await new Promise(r => setTimeout(r, 10000));
28
-
29
- console.log('[Playwright] Scrolling...');
30
- for (let i = 0; i < 3; i++) {
31
- await page.evaluate(() => window.scrollBy(0, 100));
32
- await new Promise(r => setTimeout(r, 500));
33
- console.log(' Scrolled', i + 1);
34
- }
35
-
36
- console.log('\n>>> 滚动完成,请查看配置页面');
37
- console.log('>>> 等待 5 秒后关闭...\n');
38
-
39
- await new Promise(r => setTimeout(r, 5000));
40
-
41
- console.log('[Playwright] Done!');
42
- await browser.close();
43
- }
44
-
45
- main().catch(console.error);