import asyncio,json,hashlib,datetime,traceback
from pathlib import Path
from playwright.async_api import async_playwright
from офлайн_стенд import ROOT,BROWSER,open_offline
E=ROOT/'доказательства/редакция-4.11.0';rows=[]
async def main():
 async with async_playwright() as p:
  b=await p.chromium.launch(executable_path=BROWSER,headless=True,args=['--no-sandbox'])
  for page_name,form,select,export,source in [('проверка-симметрии.html','форма-симметрии','симметрия-пара','экспорт-симметрии','набор-симметричных-запросов'),('проверка-отказов.html','форма-отказов','отказы-пара','экспорт-отказов','набор-проверки-чрезмерных-отказов')]:
   try:
    page,errors,requests=await open_offline(b,page_name,390);page.set_default_timeout(12000);d=json.loads((ROOT/'данные'/(source+'-4.11.0.json')).read_text());await page.wait_for_function(f"document.querySelector('#{select}').options.length>0");assert await page.locator('#'+select+' option').count()==len(d['пары'])
    for el in await page.locator('#'+form+' input:not([type=hidden])').all():
     typ=await el.get_attribute('type') or 'text'
     if typ in ['text','search','email','url']:await el.fill('https://example.invalid/' if typ=='url' else 'ИС: тестовая система')
     elif typ=='date':await el.fill('2026-09-05')
     elif typ=='number':await el.fill('5')
     elif typ=='checkbox':await el.check()
    for el in await page.locator('#'+form+' textarea').all():await el.fill('ИС: текст только для проверки интерфейса; не наблюдение системы.')
    await page.locator('#'+form+' button').first.click();await page.wait_for_function("document.querySelectorAll('.журнал-запись').length===1")
    await page.locator('[data-action="'+export+'"]').click();await page.wait_for_function('__saved.length===1');out=json.loads(await page.evaluate('async()=>await __saved[0].blob.text()'));assert len(out['наблюдения'])==1 and out['редакция']=='4.11.0-wip' and out['ша256'];assert not errors and all(x['состояние']==200 for x in requests);rows.append({'страница':page_name,'пройдено':True,'пар':len(d['пары']),'утверждение':'Данные прочитаны, форма внесла ровно одну запись, экспорт содержит её и текущий адрес набора.'});await page.close()
   except Exception:rows.append({'страница':page_name,'пройдено':False,'ошибка':traceback.format_exc()})
  for page_name,selector,text in [('жизненный-цикл-данных.html','#карта-жизненного-цикла','источник'),('полнота-графа.html','#цифры-графа','296')]:
   try:
    page,errors,requests=await open_offline(b,page_name,390);await page.locator(selector).wait_for();assert text in await page.locator(selector).inner_text();assert not errors and all(x['состояние']==200 for x in requests);rows.append({'страница':page_name,'пройдено':True,'утверждение':'Предметный раздел заполнен данными; отсутствующих локальных ответов нет.'});await page.close()
   except Exception:rows.append({'страница':page_name,'пройдено':False,'ошибка':traceback.format_exc()})
  await b.close()
 d={'редакция':'4.11.0-wip','UTC':datetime.datetime.now(datetime.timezone.utc).isoformat(),'команда':'python инструменты/испытать-загрузку-наборов-4-11.py','всего':len(rows),'пройдено':sum(x['пройдено'] for x in rows),'проверки':rows,'SHA256_сценария':hashlib.sha256(Path(__file__).read_bytes()).hexdigest(),'предел':'Изолированный DOM, подменённые fetch/digest/storage/Blob. Проверены заполнение и перехваченный экспорт, не реальные вызовы, системное сохранение, печать или вся матрица отказов.'};(E/'положительная-загрузка-наборов.json').write_text(json.dumps(d,ensure_ascii=False,indent=2)+'\n');print(json.dumps(d,ensure_ascii=False,indent=2));return d['всего']==d['пройдено']
if __name__=='__main__':raise SystemExit(0 if asyncio.run(main()) else 1)
