"""Final bounded actual-component consumer integration; all state and services synthetic/local.""" import json,sys,re from pathlib import Path from playwright.sync_api import sync_playwright sys.path.insert(0,'/Users/dohertj2/.codex/worktrees/scada-ui-consistency/scadaproj/components/ui-theme/validation/theme-1.0.0/canary') from browser_zoom import zoom_browser,set_browser_zoom OUT=Path('docs/ui-consistency/evidence/sb13-15/browser-final');OUT.mkdir(parents=True,exist_ok=True) def contrast(fg,bg): def lum(s): a=[float(v)/255 for v in re.findall(r'[\d.]+',s)[:3]];a=[v/12.92 if v<=.04045 else ((v+.055)/1.055)**2.4 for v in a];return sum(x*y for x,y in zip(a,[.2126,.7152,.0722])) a,b=sorted([lum(fg),lum(bg)]);return (b+.05)/(a+.05) results=[] with sync_playwright() as p: with zoom_browser(p) as (context,worker): cases=[(kind,scheme,w,z) for kind in ['audit','secrets'] for scheme in ['light','dark'] for w,z in [(1440,1),(390,1),(1440,2)]]+[(kind,scheme,w,1) for kind in ['overlays','flow'] for scheme in ['light','dark'] for w in [1440,390]] for kind,scheme,w,z in cases: page=context.new_page();page.set_viewport_size({'width':w,'height':1000});page.emulate_media(reduced_motion='reduce');errors=[];page.on('pageerror',lambda e:errors.append(str(e))) route={'audit':'batch-two','secrets':'secrets-canary?role=secrets-manager','overlays':'overlays','flow':'presentation'}[kind] page.goto('http://127.0.0.1:5201/'+route,wait_until='networkidle');page.evaluate('s=>window.sbTheme.set(s)',scheme);set_browser_zoom(worker,page,z) record={'kind':kind,'requestedWidth':w,'zoom':z,'scheme':scheme,'checks':{}} record['layout']=page.evaluate('({width:innerWidth,document:document.documentElement.scrollWidth,dpr:devicePixelRatio,scheme:document.documentElement.dataset.bsTheme})') record['checks']['noDocumentOverflow']=record['layout']['width']==record['layout']['document'];record['checks']['actualScheme']=record['layout']['scheme']==scheme if kind=='audit': more=page.locator('details[data-test=filter-more]');more.locator('summary').focus();page.keyboard.press('Enter');page.locator('#audit-execution-id').fill('not-a-guid');page.locator('#audit-actor').fill('synthetic-operator');page.locator('#audit-target').fill('synthetic-system');more.locator('summary').click();page.get_by_role('button',name='Apply',exact=True).click();page.locator('#audit-execution-id-error').wait_for() record['checks']['invalidPreserved']=page.locator('#audit-execution-id').input_value()=='not-a-guid';record['checks']['noBroadenedCallback']='No local filter applied' in page.locator('#local-filter-result').inner_text();record['checks']['visibleLinkedError']=page.locator('#audit-execution-id-error').is_visible() and page.locator('#audit-execution-id').get_attribute('aria-describedby')=='audit-execution-id-error' more.locator('summary').click();page.locator('#audit-execution-id').fill('11111111-2222-3333-4444-555555555555');more.locator('summary').click();page.get_by_role('button',name='Apply',exact=True).click();page.locator('[data-test=filter-submitted-summary]').wait_for();summary=page.locator('[data-test=filter-submitted-summary]').inner_text();record['checks']['hiddenSubmittedConstraints']=all(x in summary for x in ['Actor: synthetic-operator','Target: synthetic-system','11111111-2222-3333-4444-555555555555']) and more.evaluate('e=>!e.open');page.get_by_role('button',name='Clear',exact=True).click();page.wait_for_function("document.querySelector('#audit-actor').value === ''");page.locator('[data-test=filter-pending]').wait_for();record['checks']['clearRetainsSubmittedAndMarksDraft']=page.locator('#audit-actor').input_value()=='' and page.locator('[data-test=filter-pending]').is_visible() and 'synthetic-operator' in page.locator('[data-test=filter-submitted-summary]').inner_text() elif kind=='secrets': page.locator('[data-testid=secret-filter]').wait_for();record['checks']['metadataOnly']=page.locator('[data-secret-row]').count()==2 and page.get_by_role('button',name=re.compile('Reveal')).count()==0 pair=page.locator('caption').evaluate('e=>({color:getComputedStyle(e).color,bg:getComputedStyle(document.body).backgroundColor})');record['captionContrast']=contrast(pair['color'],pair['bg']);record['checks']['captionReadable']=record['captionContrast']>=4.5 page.locator('[data-testid=secret-filter]').fill('no-local-match');page.locator('[data-testid=filter-empty]').wait_for();page.get_by_role('button',name='Clear filter',exact=True).click();page.wait_for_function('document.querySelectorAll("[data-secret-row]").length===2');record['checks']['filterAndClear']=True page.locator('.zb-secret-technical summary').first.click();record['checks']['technicalDisclosure']=page.locator('.zb-secret-tech-scroll').first.is_visible();region=page.get_by_role('region',name='Secret metadata',exact=True);region.focus();record['checks']['namedScrollRegion']=region.get_attribute('tabindex')=='0';record['packageUi']='1.0.1' elif kind=='overlays': trigger=page.locator('#open-confirm');trigger.click();dialog=page.get_by_role('dialog');dialog.wait_for();page.wait_for_function('document.querySelector("[role=dialog]").contains(document.activeElement)');record['checks']['namedModal']=dialog.get_attribute('aria-labelledby') is not None for _ in range(5):page.keyboard.press('Tab') record['checks']['nativeTabContained']=dialog.evaluate('e=>e.contains(document.activeElement)');page.keyboard.press('Escape');dialog.wait_for(state='detached');page.wait_for_function('document.activeElement===document.querySelector("#open-confirm")');record['checks']['escapeRestoresTrigger']=True else: record['checks']['allOutcomesPresent']=page.locator('.fg-node').count()>=7 and page.locator('.ft-row').count()==7 record['checks']['thinFailureRule']=page.locator('.ft-row.failed').first.evaluate('e=>getComputedStyle(e).boxShadow').find('1px')>=0 record['checks']['pendingOpaque']=page.locator('.fg-pill.pending').evaluate('e=>getComputedStyle(e).opacity')=='1' record['errors']=errors;record['passed']=all(record['checks'].values()) and not errors;page.screenshot(path=str(OUT/f'{kind}-{scheme}-{w}-zoom{z}.png'),full_page=False);results.append(record);(OUT/'results.json').write_text(json.dumps(results,indent=2));page.close() print(json.dumps({'cases':len(results),'passed':sum(r['passed'] for r in results),'failures':[r for r in results if not r['passed']]},indent=2))