"""Run synthetic prototype checks through agent-browser, never real health services."""
import subprocess,json
from pathlib import Path
B='/Users/jack/.npm/_npx/6de2aa2fded2970c/node_modules/agent-browser/bin/agent-browser-darwin-arm64'
root=Path(__file__).resolve().parent
receipts=[]
def cli(*args):
 p=subprocess.run([B,'--session','foodmove',*args],capture_output=True,text=True,timeout=30)
 if p.returncode: raise RuntimeError(p.stdout+p.stderr)
 return p.stdout.strip()
def check(name,js):
 script="(()=>{const click=s=>{const e=document.querySelector(s);if(!e)throw Error('Missing '+s);e.click()};const text=()=>document.getElementById('app').innerText;const assert=(x,m)=>{if(!x)throw Error(m)};"+js+";return 'PASS'})()"
 out=cli('eval',script)
 if 'PASS' not in out: raise RuntimeError(name+': '+out)
 receipts.append({'test':name,'result':'PASS'})
 print(name,'PASS',flush=True)
cli('open','http://127.0.0.1:8768/prototype/food-movement-calendar.html')
cli('reload')
check('reset',"click('[data-nav=profile]');if(document.querySelector('[data-action=settings]'))click('[data-action=settings]');click('[data-action=reset]');assert(text().includes('1,100'),'baseline')")
check('T01 calendar',"click('[data-action=calendar]');assert(document.querySelectorAll('[data-date]').length===30,'month');click('[data-date=\"2026-09-07\"]');assert(text().includes('1,800'),'date total');click('[data-meal=\"1\"]');assert(text().includes('12:10'),'meal date');click('#back');assert(text().includes('9月7日'),'return date');assert(document.querySelector('[data-date=\"2026-09-09\"]').disabled,'future disabled')")
check('T02 empty date',"click('[data-action=prev-month]');click('[data-date=\"2026-08-01\"]');assert(text().includes('等待你的记录'),'empty');assert(!text().includes('1,800'),'no stale numbers');click('[data-action=next-month]');click('[data-date=\"2026-09-08\"]')")
check('T03 day balance',"click('[data-screen=balance]');assert(text().includes('当天尚未结束'),'incomplete day');assert(!text().includes('样例区间差值'),'no false net');click('#back');click('[data-date=\"2026-09-07\"]');click('[data-screen=balance]');assert(text().includes('-350 至 -50'),'complete range');click('#back');click('[data-date=\"2026-09-08\"]')")
check('T11 three lenses',"const t=document.querySelector('.daily-totals').innerText;for(const v of ['align','sit','all']){click('button[data-lens='+v+']');assert(document.querySelector('.day-canvas').dataset.lens===v,'view updates');assert(document.querySelector('.daily-totals').innerText===t,'facts unchanged');assert(document.querySelector('button[data-lens='+v+']').getAttribute('aria-pressed')==='true','accessible selection')}")
check('T04 sedentary facts',"click('[data-screen=sedentary]');assert(text().includes('175')&&text().includes('100'),'duration');assert(text().includes('低活动 · 待确认')&&text().includes('未知'),'states')")
check('T08 correction',"click('[data-id=s2]');assert(document.querySelector('.hero-number').innerText.includes('75'),'sum excludes corrected');assert(!text().includes('100分钟'),'longest recomputed');click('#back');assert(text().includes('最长连续久坐 75'),'home updated');click('[data-meal=\"1\"]');assert(text().includes('没有确认久坐'),'scene insight invalidated');click('#back')")
check('T05 form validate/save',"click('[data-action=record]');document.querySelector('#meal-energy').value='-2';assert(!document.querySelector('#meal-form').checkValidity(),'invalid blocked');document.querySelector('#meal-time').value='12:45';document.querySelector('#meal-energy').value='620';document.querySelector('#meal-form').requestSubmit();assert(text().includes('12:45')&&text().includes('620'),'saved detail');click('#back');assert(text().includes('1,040'),'home total');click('[data-screen=balance]');assert(text().includes('1040'),'balance total')")
cli('reload')
check('T05 reload persistence',"assert(text().includes('1,040'),'saved after reload');click('[data-meal=\"1\"]');assert(text().includes('12:45'),'time persists');click('#back')")
check('T09 cancel',"click('[data-action=record]');document.querySelector('#meal-energy').value='222';click('[data-action=home]');assert(text().includes('1,040'),'cancel does not submit')")
check('T06 plan lifecycle',"click('[data-nav=ai]');click('[data-action=plan]');assert(text().includes('已计划'),'plan');document.querySelector('#plan-time').value='15:30';click('[data-action=save-time]');click('[data-action=complete]');assert(text().includes('已完成（演示自报）'),'complete');click('[data-action=undo]');assert(text().includes('已计划'),'undo')")
cli('reload')
check('T06 plan persistence/cancel',"click('[data-nav=ai]');assert(document.querySelector('#plan-time').value==='15:30','plan persists');click('[data-action=cancel-action]');assert(text().includes('加入明日计划'),'cancelled')")
for mode,label in [('empty','还没有这一天'),('loading','正在整理'),('error','暂时没有加载成功'),('unauthorized','健康数据尚未授权')]:
 check('T07 '+mode,"click('[data-nav=profile]');if(document.querySelector('[data-action=settings]'))click('[data-action=settings]');document.querySelector('#state-mode').value='"+mode+"';click('[data-action=apply-mode]');assert(text().includes('"+label+"'),'mode visible');"+("click('[data-date=\"2026-09-07\"]');assert(text().includes('尚未授权'),'date cannot bypass');click('[data-nav=ai]');assert(text().includes('尚未授权'),'AI cannot bypass');" if mode=='unauthorized' else '')+"click('[data-nav=profile]');if(document.querySelector('[data-action=settings]'))click('[data-action=settings]');document.querySelector('#state-mode').value='normal';click('[data-action=apply-mode]')")
check('T07 AI failure',"click('[data-nav=profile]');if(document.querySelector('[data-action=settings]'))click('[data-action=settings]');document.querySelector('#state-mode').value='ai-error';click('[data-action=apply-mode]');click('[data-nav=ai]');assert(text().includes('AI解释暂时不可用'),'AI error');click('[data-action=ai-recover]');assert(text().includes('判断依据'),'AI recovered')")
check('T09 reset boundary',"localStorage.setItem('unrelated-demo-key','keep');click('[data-nav=profile]');if(document.querySelector('[data-action=settings]'))click('[data-action=settings]');click('[data-action=reset]');assert(localStorage.getItem('unrelated-demo-key')==='keep','other key intact');localStorage.removeItem('unrelated-demo-key');assert(text().includes('1,100'),'reset baseline')")
check('T09 storage unavailable',"window.__oldSet=Storage.prototype.setItem;Storage.prototype.setItem=function(){throw Error('simulated quota')};click('[data-action=record]');document.querySelector('#meal-time').value='12:25';document.querySelector('#meal-energy').value='650';document.querySelector('#meal-form').requestSubmit();assert(document.querySelector('[role=alert]').innerText.includes('存储不可用'),'storage failure visible');Storage.prototype.setItem=window.__oldSet;delete window.__oldSet;click('[data-nav=profile]');if(document.querySelector('[data-action=settings]'))click('[data-action=settings]');click('[data-action=reset]')")
shots=root/'evidence/screenshots';shots.mkdir(exist_ok=True)
for width,height,name in [(390,844,'mobile'),(1440,1080,'desktop'),(320,740,'small-mobile')]:
 cli('set','viewport',str(width),str(height))
 check('T10 '+name,"assert(document.documentElement.scrollWidth<=innerWidth,'root overflow');assert([...document.querySelectorAll('button')].every(b=>b.innerText.trim()||b.getAttribute('aria-label')),'button name');")
 cli('eval',"document.getElementById('toast').classList.remove('show')")
 cli('screenshot',str(shots/(name+'.png')))
cli('set','viewport','390','844')
for screen in ['balance','sedentary']:
 cli('click','[data-screen='+screen+']');cli('screenshot',str(shots/(screen+'.png')));cli('click','#back')
cli('click','[data-nav=ai]');cli('screenshot',str(shots/'ai.png'));cli('click','#back')
cli('press','Tab')
check('T10 keyboard',"assert(document.activeElement.tagName==='BUTTON','keyboard reaches buttons')")
errors=cli('errors');receipts.append({'test':'page errors','result':errors or 'none'})
if errors.strip(): raise RuntimeError(errors)
cli('open','http://127.0.0.1:8768/food-movement-calendar-prd.html')
check('PRD content parity',"assert(document.querySelectorAll('main h2').length===14,'all 14 PRD sections');assert(document.documentElement.scrollWidth<=innerWidth,'PRD mobile overflow');assert(document.querySelectorAll('table').length>=8,'tables')")
cli('screenshot',str(shots/'prd-mobile.png'))
cli('set','viewport','1440','1080');cli('screenshot',str(shots/'prd-desktop.png'))
cli('open','http://127.0.0.1:8768/prototype/food-movement-calendar.html');cli('set','viewport','390','844')
(root/'evidence/browser-checks.json').write_text(json.dumps(receipts,ensure_ascii=False,indent=2))
print('ALL',len(receipts),'CHECKS PASSED',flush=True)
