42 lines
6.3 KiB
Python
42 lines
6.3 KiB
Python
"""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))
|