"""Sleep-led Oura benchmark slice: synthetic data and scoped stage service."""
import subprocess,json
from pathlib import Path
B='/Users/jack/.npm/_npx/6de2aa2fded2970c/node_modules/agent-browser/bin/agent-browser-darwin-arm64'
r=Path(__file__).resolve().parent;results=[]
def cli(*args):
 p=subprocess.run([B,'--session','ouraslice',*args],text=True,capture_output=True,timeout=30)
 if p.returncode:raise RuntimeError(p.stdout+p.stderr)
 return p.stdout.strip()
def check(name,body):
 code="(()=>{const click=s=>{const x=document.querySelector(s);if(!x)throw Error('missing '+s);x.click()};const t=()=>document.querySelector('#app').innerText;const assert=(x,m)=>{if(!x)throw Error(m)};"+body+";return 'PASS'})()"
 assert 'PASS' in cli('eval',code)
 results.append({'test':name,'result':'PASS'});print(name,'PASS',flush=True)
cli('close');cli('open','http://127.0.0.1:8768/prototype/food-movement-calendar.html?v=6');cli('set','viewport','390','844')
check('reset and sleep first',"click('[data-nav=profile]');click('[data-action=settings]');click('[data-action=reset]');assert(document.querySelector('.sleep-hero'),'sleep entry');assert(t().includes('合成样例'),'sample disclosed')")
check('golden sleep durations',"const expected={m01:[420,430],m02:[365,385]};for(const id of ['m01','m02']){for(const [i,date] of ['2026-09-08','2026-09-07'].entries()){const x=FM_DATA.members[id].sleepRecords[date];const asleep=x.stages.filter(s=>s.type!=='awake').reduce((n,s)=>n+s.minutes,0);assert(asleep===expected[id][i],'sleep sum');assert(x.stages.reduce((n,s)=>n+s.minutes,0)===x.totalMinutes,'stage sum');assert(x.endAt-x.startAt===x.totalMinutes*60000,'duration');assert(x.stages.every((s,i)=>i===0?s.startAt===x.startAt:s.startAt===x.stages[i-1].endAt),'no gaps/overlap')}}")
check('sleep detail and night switching',"click('[data-screen=sleep]');assert(t().includes('睡眠420 + 清醒50 = 记录时段470'),'detail math');click('[data-sleep-date=\"2026-09-07\"]');assert(t().includes('睡眠430 + 清醒50 = 记录时段480'),'other night');click('[data-sleep-date=\"2026-09-08\"]')")
check('readiness no invented score',"click('[data-action=readiness]');assert(t().includes('尚未形成综合评分')&&t().includes('暂无可读数据'),'no fake readiness');click('[data-nav=home]');click('[data-screen=daily-support]')")
check('personal focus',"click('[data-focus=daily-movement]');assert(document.querySelector('[data-focus=daily-movement]').getAttribute('aria-pressed')==='true','focus saved');")
cli('reload')
check('focus persists',"click('[data-screen=daily-support]');assert(document.querySelector('[data-focus=daily-movement]').getAttribute('aria-pressed')==='true','focus survives reload')")
check('stage service explicit consent',"click('[data-action=nutrition-session]');assert(t().includes('待安排')&&t().includes('不承诺每日一对一'),'resource boundary');document.querySelector('#session-question').value='我希望在阶段讲座了解怎样把晚餐与下班活动安排好。';assert(!document.querySelector('#session-question-form').checkValidity(),'must consent');document.querySelector('#share-question').checked=true;document.querySelector('#session-question-form').requestSubmit();assert(t().includes('问题已保存'),'question saved')")
cli('tab','new','http://127.0.0.1:8768/prototype/pc.html?v=6');cli('set','viewport','1440','1000')
check('stage review sees authorized question',"click('[data-nav=nutrition-session]');assert(t().includes('我希望在阶段讲座'),'question in expert view');assert(!t().includes('周禾'),'no unshared member')")
check('PC member isolation and focus readonly',"click('[data-nav=home]');click('[data-nav=sleep]');assert(t().includes('睡眠420'),'A sleep');const p=document.querySelector('#member-picker');p.value='m02';p.dispatchEvent(new Event('change',{bubbles:true}));click('[data-nav=sleep]');assert(t().includes('睡眠365'),'B separate');click('[data-nav=daily-support]');click('[data-focus=sleep-routine]');assert(document.querySelector('[data-focus=regular-meals]').getAttribute('aria-pressed')==='true','professional cannot change focus')")
check('unauthorized sleep hidden',"const p=document.querySelector('#member-picker');p.value='m03';p.dispatchEvent(new Event('change',{bubbles:true}));click('[data-nav=sleep]');assert(t().includes('尚未获得健康数据分享授权')&&!document.querySelector('.sleep-chart'),'sleep gated')")
cli('tab','0');cli('set','viewport','390','844')
check('missing sleep supported',"click('[data-nav=home]');click('[data-action=calendar]');click('[data-date=\"2026-09-06\"]');assert(t().includes('还没有睡眠记录'),'no invented night');click('[data-screen=sleep]');assert(t().includes('暂无这一夜'),'detail missing')")
check('uncomfortable state no activity advice',"click('[data-nav=home]');click('[data-date=\"2026-09-08\"]');click('[data-screen=checkin]');click('[data-action=discomfort]');click('[data-nav=home]');click('[data-screen=readiness]');assert(t().includes('本人已反馈不适'),'feedback seen');click('[data-nav=ai]');assert(!document.querySelector('[data-action=plan]'),'no added activity advice')")
shots=r/'evidence/screenshots';shots.mkdir(exist_ok=True)
for width in [390,320]:
 cli('set','viewport',str(width),'900');cli('click','[data-nav=home]')
 for page in ['home','sleep','daily-support','nutrition-session']:
  if page=='nutrition-session':
   cli('click','[data-screen=daily-support]');cli('click','[data-action=nutrition-session]')
  elif page!='home':cli('click','[data-screen='+page+']')
  check(str(width)+' '+page+' layout',"assert(document.documentElement.scrollWidth<=innerWidth,'overflow');assert(!t().includes('NaN'),'number validity')")
  cli('screenshot',str(shots/f'oura-{width}-{page}.png'));cli('click','[data-nav=home]')
cli('tab','1');cli('eval',"const p=document.querySelector('#member-picker');p.value='m01';p.dispatchEvent(new Event('change',{bubbles:true}))");cli('set','viewport','1440','1000');cli('click','[data-nav=sleep]')
check('PC sleep layout',"assert(document.documentElement.scrollWidth<=innerWidth,'PC overflow')")
cli('screenshot',str(shots/'oura-pc-sleep.png'));cli('click','[data-nav=nutrition-session]');cli('screenshot',str(shots/'oura-pc-stage-review.png'))
errors=cli('errors');assert not errors,errors
(r/'evidence/oura-slice-checks.json').write_text(json.dumps(results,ensure_ascii=False,indent=2))
print('ALL',len(results),'CHECKS PASSED',flush=True)
