verify-voc-report-browser.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. """Local report layout/link check, separate from production board acceptance."""
  2. from pathlib import Path
  3. import base64
  4. import json
  5. import subprocess
  6. import tempfile
  7. import time
  8. import websocket
  9. from urllib.request import urlopen
  10. from urllib.parse import urlparse,unquote
  11. ROOT=Path(__file__).resolve().parents[1]
  12. WORK=ROOT/'work/voc-structure-20261105'
  13. REPORT=ROOT/'artifacts/康师傅VOC结构化入库-20261105/结构化入库与Agent规则报告.html'
  14. profile=Path(tempfile.mkdtemp(prefix='r2-report-browser-',dir=WORK))
  15. process=subprocess.Popen(['C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe','--headless=new','--disable-gpu',
  16. '--remote-debugging-port=0','--disable-background-networking','--disable-component-update','--disable-sync','--disable-extensions',
  17. '--no-first-run','--no-default-browser-check','--user-data-dir='+str(profile),'about:blank'],
  18. stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,creationflags=subprocess.CREATE_NO_WINDOW)
  19. ws=None;serial=0
  20. try:
  21. end=time.monotonic()+20
  22. while not (profile/'DevToolsActivePort').exists():
  23. assert time.monotonic()<end and process.poll() is None
  24. time.sleep(.1)
  25. port=(profile/'DevToolsActivePort').read_text().splitlines()[0]
  26. page=next(x for x in json.load(urlopen('http://127.0.0.1:'+port+'/json/list',timeout=5)) if x['type']=='page')
  27. ws=websocket.create_connection(page['webSocketDebuggerUrl'],timeout=20,suppress_origin=True,http_no_proxy=['127.0.0.1','localhost'])
  28. def call(method,params=None):
  29. global serial
  30. serial+=1;rid=serial;ws.send(json.dumps(dict(id=rid,method=method,params=params or {})))
  31. while True:
  32. response=json.loads(ws.recv())
  33. if response.get('id')==rid:
  34. assert 'error' not in response,response
  35. return response.get('result',{})
  36. def evaluate(expression):
  37. response=call('Runtime.evaluate',dict(expression=expression,returnByValue=True))
  38. assert 'exceptionDetails' not in response,response
  39. return response['result'].get('value')
  40. call('Page.enable');call('Page.navigate',dict(url=REPORT.as_uri()))
  41. end=time.monotonic()+10
  42. while evaluate('document.readyState')!='complete':
  43. assert time.monotonic()<end
  44. time.sleep(.05)
  45. results=[]
  46. for width,height in [(1440,1000),(390,844),(320,740)]:
  47. call('Emulation.setDeviceMetricsOverride',dict(width=width,height=height,deviceScaleFactor=1,mobile=width<700))
  48. metrics=evaluate("""(()=>({width:innerWidth,documentWidth:document.documentElement.scrollWidth,title:document.title,h1:document.querySelector('h1').textContent,sections:document.querySelectorAll('section').length,scenarioRows:document.querySelectorAll('section')[3].querySelectorAll('tr').length-1,links:[...document.querySelectorAll('a')].map(a=>a.href)}))()""")
  49. assert metrics['documentWidth']<=width,metrics
  50. assert metrics['scenarioRows']==13,metrics
  51. for link in metrics['links']:
  52. path=unquote(urlparse(link).path)
  53. if path.startswith('/') and path[2:3]==':':path=path[1:]
  54. assert Path(path).is_file(),link
  55. image=call('Page.captureScreenshot',dict(format='png',captureBeyondViewport=False))
  56. (WORK/f'report-r2-{width}.png').write_bytes(base64.b64decode(image['data']))
  57. results.append(metrics)
  58. result=dict(ok=True,report=str(REPORT),localReportBrowserVerified=True,productionBoardBrowserVerified=False,widths=results)
  59. (WORK/'report-r2-browser-validation.json').write_text(json.dumps(result,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
  60. print(json.dumps(dict(ok=True,widths=[x['width'] for x in results],noDocumentOverflow=True,localLinksVerified=True,scenarios=13,productionBoardBrowserVerified=False),ensure_ascii=False,indent=2))
  61. finally:
  62. if ws:ws.close()
  63. process.terminate()
  64. try:process.wait(timeout=8)
  65. except subprocess.TimeoutExpired:process.kill()