"""Check reviewer-draft behavior in an isolated browser session, never user data."""
from pathlib import Path
import subprocess,json
R=Path(__file__).parent
B='/Users/jack/.npm/_npx/6de2aa2fded2970c/node_modules/agent-browser/bin/agent-browser-darwin-arm64'
results=[]
def cli(*args):
 p=subprocess.run([B,'--session','cpt-review-check',*args],capture_output=True,text=True,timeout=30)
 if p.returncode:raise RuntimeError(p.stdout+p.stderr)
 return p.stdout.strip()
def test(name,js):
 out=cli('eval',"(async()=>{const assert=(x,m)=>{if(!x)throw Error(m)};"+js+";return 'PASS'})()")
 assert 'PASS' in out,(name,out);results.append({'test':name,'result':'PASS'});print(name,'PASS',flush=True)
cli('open','http://127.0.0.1:8768/review/requirements-prototype-review.html');cli('set','viewport','1440','1000')
test('45 requirements and 32 screen records',"assert(document.querySelectorAll('.req-item').length===45,'requirements');assert(document.querySelectorAll('#screen-table tbody tr').length===32,'screens');assert(document.querySelector('#progress').textContent.includes('45'),'progress')")
test('future requirements not reported as implemented',"document.querySelector('#start-review').click();document.querySelector('#search').value='餐单';document.querySelector('#search').dispatchEvent(new Event('input'));assert(document.querySelector('#detail').textContent.includes('每日个性化餐单'),'filtered selection');assert(document.querySelector('#preview-warning').textContent.includes('不代表'),'gap visible');assert(document.querySelector('#opinion').value==='pending','not preapproved')")
test('opinion and draft safe rendering',"document.querySelector('#opinion').value='revise';document.querySelector('#opinion').dispatchEvent(new Event('change'));document.querySelector('#comment').value='验收测试意见 <img src=x onerror=window.reviewInjected=1>';document.querySelector('#comment').dispatchEvent(new Event('input'));document.querySelector('#proposed-rule').value='测试草稿：售罄时先提供可供应替代';document.querySelector('#proposed-rule').dispatchEvent(new Event('input'));assert(document.querySelector('#progress').textContent.includes('1 项'),'draft progress')")
cli('reload')
test('reload preserves feedback without rewriting original rule',"document.querySelector('#start-review').click();document.querySelector('#search').value='REQ-R30';document.querySelector('#search').dispatchEvent(new Event('input'));assert(document.querySelector('#opinion').value==='revise','saved opinion');assert(document.querySelector('#comment').value.includes('<img'),'saved literal');assert(!document.querySelector('#detail img')&&!window.reviewInjected,'no injection');assert(document.querySelector('#detail dd').textContent.includes('真实菜单'),'original unchanged');assert(document.querySelector('#proposed-rule').value.includes('售罄'),'draft separate')")
test('JSON and Markdown export all rows and user draft',"const old=URL.createObjectURL,click=HTMLAnchorElement.prototype.click;window.reviewBlobs=[];URL.createObjectURL=b=>{window.reviewBlobs.push(b);return 'blob:test'};HTMLAnchorElement.prototype.click=()=>{};document.querySelector('#export-json').click();document.querySelector('#export-md').click();const j=JSON.parse(await window.reviewBlobs[0].text()),m=await window.reviewBlobs[1].text();assert(j.items.length===45&&j.status==='reviewer-draft-not-reconciled','scope');assert(j.items.find(x=>x.id==='REQ-R30').review.opinion==='revise','opinion exported');assert(m.includes('REQ-R45')&&m.includes('售罄时'),'markdown');URL.createObjectURL=old;HTMLAnchorElement.prototype.click=click")
test('coverage filter and empty search state',"document.querySelector('#search').value='';document.querySelector('#search').dispatchEvent(new Event('input'));document.querySelector('#coverage').value='blocked';document.querySelector('#coverage').dispatchEvent(new Event('change'));assert(document.querySelectorAll('.req-item').length===5,'blocked count');document.querySelector('#search').value='__nothing__';document.querySelector('#search').dispatchEvent(new Event('input'));assert(document.querySelector('#detail').textContent.includes('没有匹配需求'),'empty explicit');document.querySelector('#search').value='';document.querySelector('#search').dispatchEvent(new Event('input'));document.querySelector('#coverage').value='all';document.querySelector('#coverage').dispatchEvent(new Event('change'));")
test('PC iframe is interactive professional workspace',"const f=document.querySelector('#prototype');const done=new Promise(r=>f.addEventListener('load',r,{once:true}));document.querySelector('[data-mode=pc]').click();await done;assert(f.contentDocument.body.dataset.surface==='pc','professional page');assert(f.contentDocument.querySelector('#member-picker'),'member selection');assert(document.querySelector('#open-prototype').href.endsWith('/prototype/pc.html'),'correct independent link')")
cli('screenshot','/tmp/cpt-review-20260911/review-desktop.png')
test('mobile frame mode switches back',"const f=document.querySelector('#prototype');const done=new Promise(r=>f.addEventListener('load',r,{once:true}));document.querySelector('[data-mode=mobile]').click();await done;assert(f.contentDocument.querySelector('.tabbar'),'mobile nav');assert(!document.querySelector('#frame-shell').classList.contains('pc'),'mode')")
cli('set','viewport','390','844')
for tab in ['overview','workbench','screens','documents']:
 test('mobile '+tab+' layout',f"document.querySelector('[data-tab={tab}]').click();assert(document.documentElement.scrollWidth<=innerWidth,'overflow')")
cli('click','[data-tab=overview]');cli('screenshot','/tmp/cpt-review-20260911/review-mobile.png')
cli('eval', "localStorage.setItem('cpt-requirements-review-20260911-r1','{broken')")
cli('reload')
test('corrupt stored draft is preserved and new notes stay exportable', "document.querySelector('#start-review').click();document.querySelector('#comment').value='new in-memory draft';document.querySelector('#comment').dispatchEvent(new Event('input'));assert(localStorage.getItem('cpt-requirements-review-20260911-r1')==='{broken','old data preserved');assert(document.querySelector('#notice').textContent.includes('先导出'),'export guidance');")
# This key is new and only synthetic test notes are cleared in this isolated session.
cli('eval',"localStorage.removeItem('cpt-requirements-review-20260911-r1')")
cli('reload')
errors=cli('errors');assert not errors,errors
(R/'review-browser-checks.json').write_text(json.dumps(results,ensure_ascii=False,indent=2)+'\n')
print('ALL',len(results),'REVIEW CHECKS PASSED',flush=True)
