b15e1c9541
- P0: useSpeechSynthesis.ts cancel()增加isSupported守卫 - P0: iot_provider.go 添加loader nil检查防止NPE panic - 新增CDP E2E v4测试脚本 14项全绿通过 - 生成第二轮修复报告 docs/debug/2026-05-21-round2-fixes.md
153 lines
4.5 KiB
Python
153 lines
4.5 KiB
Python
#!/usr/bin/env python3
|
|
"""CDP E2E Test v2: 使用真实JWT Token进行前端端到端测试"""
|
|
import json, time, base64, urllib.request
|
|
|
|
REAL_TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3ODE5Njg1MDAsImlhdCI6MTc3OTM3NjUwMCwidXNlcl9pZCI6ImFkbWluIn0.dHdo1NciPDC8-yR0P-CHv2x0hsh3-G2sHOr9E8VBvws'
|
|
|
|
print('=== CDP E2E Test v2 ===')
|
|
|
|
# Open page
|
|
req = urllib.request.Request('http://127.0.0.1:9225/json/new?url=http://localhost:5199/', method='PUT')
|
|
resp = urllib.request.urlopen(req, timeout=10)
|
|
page = json.loads(resp.read())
|
|
ws_url = page['webSocketDebuggerUrl']
|
|
page_id = page['id']
|
|
print(f'Page: {page_id}')
|
|
|
|
from websocket import create_connection
|
|
ws = create_connection(ws_url, timeout=10)
|
|
|
|
def cdp(method, params=None, msg_id=1):
|
|
ws.send(json.dumps({'id': msg_id, 'method': method, 'params': params or {}}))
|
|
|
|
def recv_all(timeout=3):
|
|
ws.settimeout(timeout)
|
|
msgs = []
|
|
try:
|
|
while True:
|
|
msgs.append(ws.recv())
|
|
except:
|
|
pass
|
|
return msgs
|
|
|
|
def find_result(msgs, msg_id):
|
|
for m in msgs:
|
|
try:
|
|
d = json.loads(m)
|
|
if d.get('id') == msg_id:
|
|
return d.get('result', {})
|
|
except:
|
|
pass
|
|
return None
|
|
|
|
# Enable domains
|
|
cdp('Page.enable', {}, 1)
|
|
cdp('Runtime.enable', {}, 2)
|
|
cdp('Network.enable', {}, 3)
|
|
cdp('Log.enable', {}, 4)
|
|
recv_all(1)
|
|
|
|
# Inject real token
|
|
print('\n--- Inject Token ---')
|
|
cdp('Runtime.evaluate', {
|
|
'expression': 'localStorage.setItem("token", "%s"); localStorage.setItem("user_id", "admin"); "OK"' % REAL_TOKEN,
|
|
'returnByValue': True
|
|
}, 50)
|
|
recv_all(2)
|
|
|
|
# Reload
|
|
print('\n--- Reload Page ---')
|
|
cdp('Page.navigate', {'url': 'http://localhost:5199/'}, 60)
|
|
time.sleep(4)
|
|
all_msgs = recv_all(5)
|
|
|
|
# Analyze
|
|
print('\n--- Console Analysis ---')
|
|
errors = []
|
|
for m in all_msgs:
|
|
try:
|
|
d = json.loads(m)
|
|
except:
|
|
continue
|
|
method = d.get('method', '')
|
|
params = d.get('params', {})
|
|
|
|
if method == 'Log.entryAdded':
|
|
entry = params.get('entry', {})
|
|
lvl = entry.get('level', 'log')
|
|
text = str(entry.get('text', ''))[:200]
|
|
if lvl == 'error':
|
|
errors.append(text)
|
|
print(f' [{lvl}] {text}')
|
|
|
|
elif method == 'Runtime.consoleAPICalled':
|
|
args = params.get('args', [])
|
|
lvl = params.get('type', 'log')
|
|
texts = [str(a.get('value', '') or a.get('description', ''))[:150] for a in args]
|
|
combined = ' '.join(texts)
|
|
if lvl == 'error':
|
|
errors.append(combined)
|
|
print(f' [{lvl}] {combined}')
|
|
|
|
elif method == 'Runtime.exceptionThrown':
|
|
exc = params.get('exceptionDetails', {})
|
|
text = exc.get('text', '')
|
|
errors.append(text)
|
|
print(f' [EXCEPTION] {text}')
|
|
|
|
# Network
|
|
print('\n--- Network API Calls ---')
|
|
for m in all_msgs:
|
|
try:
|
|
d = json.loads(m)
|
|
except:
|
|
continue
|
|
method = d.get('method', '')
|
|
params = d.get('params', {})
|
|
if method == 'Network.responseReceived':
|
|
resp = params.get('response', {})
|
|
url = resp.get('url', '')
|
|
status = resp.get('status', 0)
|
|
if '/api/' in url:
|
|
print(f' [{status}] {url[-80:]}')
|
|
elif method == 'Network.loadingFailed':
|
|
print(f' [FAIL] {params.get("errorText","?")}')
|
|
|
|
# Runtime checks
|
|
print('\n--- Runtime State ---')
|
|
checks = [
|
|
'localStorage.getItem("token") ? "TOKEN_OK:" + localStorage.getItem("token").slice(0,20) + "..." : "NO_TOKEN"',
|
|
'localStorage.getItem("user_id") || "none"',
|
|
'document.title',
|
|
'document.querySelectorAll("[class*=\\"message\\"]").length + " message elements"',
|
|
'(function(){try{var s=document.querySelector("#root");return s?s.children.length+" root children":"no root"}catch(e){return e.message}})()',
|
|
]
|
|
for idx, expr in enumerate(checks):
|
|
cdp('Runtime.evaluate', {'expression': expr, 'returnByValue': True}, 200 + idx)
|
|
recv_msgs = recv_all(2)
|
|
r = find_result(recv_msgs, 200 + idx)
|
|
if r:
|
|
val = r.get('result', {}).get('value', '?')
|
|
print(f' {val}')
|
|
|
|
# Screenshot
|
|
print('\n--- Screenshot ---')
|
|
cdp('Page.captureScreenshot', {'format': 'png'}, 300)
|
|
recv_msgs = recv_all(5)
|
|
r = find_result(recv_msgs, 300)
|
|
if r and r.get('data'):
|
|
img = base64.b64decode(r['data'])
|
|
with open('/tmp/cyrene_e2e_v2.png', 'wb') as f:
|
|
f.write(img)
|
|
print(f' Saved: {len(img)} bytes')
|
|
|
|
if errors:
|
|
print(f'\n=== {len(errors)} ERRORS ===')
|
|
for e in errors:
|
|
print(f' - {e[:200]}')
|
|
else:
|
|
print('\n=== ZERO ERRORS ===')
|
|
|
|
ws.close()
|
|
print('[DONE]')
|