"""Chromium fallback after T3 preview explicitly reported no automation host.""" import subprocess,tempfile,time,json,urllib.request,base64,shutil from pathlib import Path import websocket O=Path('/opt/neiro/reports/pets-knp-artifacts');O.mkdir(exist_ok=True);profile=tempfile.mkdtemp(prefix='pets-cdp-');log=(O/'chromium.log').open('w');p=subprocess.Popen(['chromium','--headless','--no-sandbox','--disable-gpu','--remote-debugging-port=9342','--remote-allow-origins=*','--user-data-dir='+profile,'about:blank'],stdout=log,stderr=log);ws=None;counter=0;errors=[] try: for _ in range(50): try:pages=json.load(urllib.request.urlopen('http://127.0.0.1:9342/json',timeout=1));break except Exception:time.sleep(.1) ws=websocket.create_connection(next(a['webSocketDebuggerUrl'] for a in pages if a['type']=='page'),timeout=30) def call(method,params=None): global counter counter+=1;want=counter;ws.send(json.dumps({'id':want,'method':method,'params':params or {}})) while True: msg=json.loads(ws.recv()) if msg.get('method')=='Runtime.exceptionThrown':errors.append(msg['params']) if msg.get('id')==want: if 'error' in msg:raise RuntimeError(msg['error']) return msg.get('result',{}) def js(expr,wait=False): out=call('Runtime.evaluate',{'expression':expr,'returnByValue':True,'awaitPromise':wait}) if 'exceptionDetails' in out:raise RuntimeError(out['exceptionDetails']) return out['result'].get('value') def until(expr):return js('new Promise((resolve,reject)=>{let n=0;let t=setInterval(()=>{if('+expr+'){clearInterval(t);resolve(true)}else if(++n>200){clearInterval(t);reject(Error("wait timeout"))}},50)})',True) call('Page.enable');call('Runtime.enable');call('Emulation.setDeviceMetricsOverride',{'width':1440,'height':1100,'deviceScaleFactor':1,'mobile':False});call('Page.navigate',{'url':'http://10.9.8.230:8082/'}) until("document.querySelectorAll('.sample').length===24&&document.querySelector('#accuracy').textContent.includes('%')") js("document.querySelector('.sample').click()");until("!busy&&document.querySelector('#status').textContent.includes('ГОТОВО')") first=js("({answer:document.querySelector('#answer').textContent,status:document.querySelector('#status').textContent,subtitle:document.querySelector('#subtitle').textContent,overflow:document.documentElement.scrollWidth>innerWidth})");assert not first['overflow'] shot=call('Page.captureScreenshot',{'format':'png','captureBeyondViewport':True});(O/'pets-desktop.png').write_bytes(base64.b64decode(shot['data'])) # Native file picker input using a real local file, then classify the image. samples=json.load(urllib.request.urlopen('http://10.9.8.230:8082/api/samples'));file=O/'upload-test.png';file.write_bytes(base64.b64decode(samples[1]['image'].split(',')[1]));doc=call('DOM.getDocument');node=call('DOM.querySelector',{'nodeId':doc['root']['nodeId'],'selector':'#file'})['nodeId'];call('DOM.setFileInputFiles',{'nodeId':node,'files':[str(file)]});until('truth===null&¤t!==null&&!busy');js("document.querySelector('#run').click()");until("!busy&&document.querySelector('#status').textContent.includes('ГОТОВО')") upload=js("({answer:document.querySelector('#answer').textContent,subtitle:document.querySelector('#subtitle').textContent,hasInput:document.querySelector('#tiny').src.startsWith('data:image/png')})");assert upload['hasInput'] call('Emulation.setDeviceMetricsOverride',{'width':390,'height':844,'deviceScaleFactor':1,'mobile':True});time.sleep(.2);mobile=js("({overflow:document.documentElement.scrollWidth>innerWidth,samples:document.querySelectorAll('.sample').length})");assert not mobile['overflow'];shot=call('Page.captureScreenshot',{'format':'png','captureBeyondViewport':True});(O/'pets-mobile.png').write_bytes(base64.b64decode(shot['data'])) # Select a new picture while an old request is running: old answer must not overwrite it. js("document.querySelector('#run').click();select(document.querySelector('.sample img').src,null)");until('!busy');stale=js("({answer:document.querySelector('#answer').textContent,status:document.querySelector('#status').textContent})");assert stale['answer']=='Готовы?' assert not errors,errors (O/'browser-checks.json').write_text(json.dumps({'desktop':first,'file_upload':upload,'mobile':mobile,'stale_response_ignored':True,'javascript_errors':errors},ensure_ascii=False,indent=2));print(json.dumps({'browser_checked':True,'javascript_errors':len(errors),'mobile_overflow':mobile['overflow']})) finally: if ws:ws.close() p.terminate() try:p.wait(timeout=5) except subprocess.TimeoutExpired:p.kill();p.wait() log.close();shutil.rmtree(profile,ignore_errors=True)