import asyncio, pathlib, json from playwright.async_api import async_playwright ROOT=pathlib.Path(__file__).resolve().parent OUT=pathlib.Path('/mnt/data') async def main(): async with async_playwright() as p: browser=await p.chromium.launch(headless=True,executable_path='/usr/bin/chromium',args=['--no-sandbox']) page=await browser.new_page(viewport={'width':1512,'height':1020},accept_downloads=True) errs=[];page.on('pageerror',lambda e:errs.append(str(e))) await page.set_content((ROOT/'index.html').read_text(),wait_until='domcontentloaded') await page.add_style_tag(content=(ROOT/'styles.css').read_text()) blob=(ROOT/'robustness_snapshots.json').read_text() await page.evaluate('''(blob)=>{window.fetch=async (uri)=>String(uri).endsWith('robustness_snapshots.json')?{ok:true,json:async()=>JSON.parse(blob)}:{ok:false,status:404};}''',blob) await page.add_script_tag(content=(ROOT/'evalflow_engine.js').read_text()) await page.add_script_tag(content=(ROOT/'app.js').read_text()) await page.wait_for_function("document.querySelector('#dataStatus').textContent==='SNAPSHOT LOADED'",timeout=40000) checks=[] async def ck(name,expr): if not expr:raise AssertionError(name+'; browser errors='+repr(errs)) checks.append(name);print('PASS',name) await ck('hero displays EvalFlow','EvalFlow' in await page.locator('h1').inner_text()) await ck('source 4416 records','4,416' in await page.locator('#heroRecords').inner_text()) await ck('nine research views',await page.locator('.nav-btn').count()==9) await ck('default decision PASS','PASS' in await page.locator('#content').inner_text()) await ck('default hybrid accuracy 77.8%', '77.8%' in await page.locator('#content').inner_text()) await ck('default run registry starts with one', await page.locator('#dataStatus').inner_text()=='SNAPSHOT LOADED') await page.locator('#baseline').select_option('hybrid') await page.locator('#candidate').select_option('nb') await page.locator('#run').click() await ck('regression candidate BLOCK','BLOCK' in await page.locator('#content').inner_text()) await page.locator('#baseline').select_option('nb') await page.locator('#candidate').select_option('hybrid') await page.locator('#run').click() await ck('recovery to passing candidate','ALL GATES PASS' in await page.locator('#content').inner_text()) await page.locator('[data-view=pipeline]').click() await ck('seven pipeline stages',await page.locator('.step').count()==7) await ck('CLI displayed','node ci_evalflow.js' in await page.locator('#content').inner_text()) await page.locator('[data-view=regressions]').click() await ck('regression audit cases displayed',await page.locator('.case').count()>0) await page.locator('#caseFilter').select_option('improved') await ck('improvement filter works',await page.locator('.case').count()>0) await page.locator('#caseSearch').fill('ari-') await ck('case search works',await page.locator('.case').count()>0) await page.locator('[data-view=gates]').click() await ck('gate policy editor 8 fields',await page.locator('input[id^=policy_]').count()==8) await page.locator('#policy_minAccuracy').fill('0.95') await page.locator('#applyPolicy').click() await ck('strict gate policy blocks','BLOCK' in await page.locator('#content').inner_text()) await page.locator('#resetPolicy').click() await ck('reset gate policy passes','PASS' in await page.locator('#content').inner_text()) await page.locator('[data-view=slices]').click() await ck('slices show 3 languages','Português' not in await page.locator('#content').inner_text() or await page.locator('table tbody tr').count()==3) await page.locator('#sliceKey').select_option('domain') await ck('single mixed-domain challenge warning', 'domain-to-domain comparisons' in await page.locator('#content').inner_text()) await page.locator('[data-view=stress]').click() await ck('six stress test conditions',await page.locator('table tbody tr').count()==6) await ck('destructive edit unscored','Not scored' in await page.locator('#content').inner_text()) await page.locator('[data-view=registry]').click() await ck('run registry accessible',await page.locator('table tbody tr').count()>=3) await page.locator('[data-view=automation]').click() await ck('real CI commands shown','node test_engine.js' in await page.locator('#content').inner_text()) await ck('optional GitHub workflow warning','not enabled' in (await page.locator('#content').inner_text()).lower()) await page.locator('[data-view=export]').click() for kind,ext in [('report','.md'),('result','.json'),('paired','.csv'),('gates','.csv'),('registry','.json')]: async with page.expect_download() as d:await page.locator(f'[data-export={kind}]').click() item=await d.value await ck('export '+kind,item.suggested_filename.endswith(ext)) original=json.loads(blob) small={'rows':[r for r in original['rows'] if not (r['method']=='hybrid' and r['split']=='challenge' and r['attack']=='typo' and r['id']==original['rows'][-1]['id'])]} await page.locator('#importJson').set_input_files({'name':'research_import.json','mimeType':'application/json','buffer':json.dumps(small).encode()}) await page.wait_for_function("document.querySelector('#dataStatus').textContent.includes('LOCAL IMPORT')") await ck('import custom JSON locally',await page.locator('#dataStatus').inner_text()=='LOCAL IMPORT · UNVERIFIED SOURCE') await page.locator('#restore').click() await page.wait_for_function("document.querySelector('#dataStatus').textContent==='SNAPSHOT LOADED'") await ck('restore bundled evidence',await page.locator('#dataStatus').inner_text()=='SNAPSHOT LOADED') await page.locator('[data-view=overview]').click() await page.evaluate('window.scrollTo(0,0)') await page.wait_for_timeout(150) await page.screenshot(path=str(OUT/'EvalFlow_CI_Lab_DESKTOP_PREVIEW.png'),full_page=True) await ck('desktop no sideways scroll',await page.evaluate('document.documentElement.scrollWidth <= window.innerWidth+3')) await page.set_viewport_size({'width':390,'height':844}) await page.evaluate('scrollTo(0,0)') await page.screenshot(path=str(OUT/'EvalFlow_CI_Lab_MOBILE_PREVIEW.png'),full_page=True) await ck('mobile no sideways scroll',await page.evaluate('document.documentElement.scrollWidth <= window.innerWidth+3')) await ck('mobile navigation accessible',await page.locator('[data-view=export]').is_visible()) await ck('no browser JavaScript errors',len(errs)==0) print('TOTAL BROWSER CHECKS',len(checks),'PASS') await browser.close() asyncio.run(main())