refactor(ui): prioritize permitted operational views and touch topology
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
"""Bounded synthetic actual-component SB10–12 check; no live/fleet operations."""
|
||||
import asyncio,json,sys
|
||||
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/sb10-12/browser-final');OUT.mkdir(parents=True,exist_ok=True)
|
||||
def main():
|
||||
results=[]
|
||||
with sync_playwright() as p:
|
||||
with zoom_browser(p) as (context,worker):
|
||||
for kind in ['topology','health']:
|
||||
for scheme in ['light','dark']:
|
||||
for w,h,zoom in [(1440,900,1),(768,480,1),(390,844,1),(1440,900,2)]:
|
||||
page=context.new_page();page.set_viewport_size({'width':w,'height':h})
|
||||
errors=[];page.on('pageerror',lambda e:errors.append(str(e)))
|
||||
page.add_init_script("localStorage.setItem('sb-theme', '"+scheme+"')")
|
||||
route='topology-canary?role=Deployer' if kind=='topology' else 'operations-canary?role=Viewer'
|
||||
page.goto('http://127.0.0.1:5201/'+route,wait_until='networkidle');page.evaluate('s => window.sbTheme.set(s)',scheme)
|
||||
page.locator('.tv-root' if kind=='topology' else '[data-site-id]').first.wait_for()
|
||||
page.wait_for_timeout(500)
|
||||
set_browser_zoom(worker,page,zoom)
|
||||
data=page.evaluate('''() => ({width:innerWidth,dpr:devicePixelRatio,document:document.documentElement.scrollWidth,scheme:document.documentElement.dataset.bsTheme,headings:[...document.querySelectorAll('h1')].map(e=>e.textContent),sites:[...document.querySelectorAll('[data-site-id]')].map(e=>e.dataset.siteId),summary:document.querySelector('[aria-label="Site observations"]')?.innerText,tree:document.querySelector('.topology-tree')&&{height:document.querySelector('.topology-tree').clientHeight,scroll:document.querySelector('.topology-tree').scrollHeight,overflow:getComputedStyle(document.querySelector('.topology-tree')).overflowY},rows:[...document.querySelectorAll('.tv-row')].slice(0,5).map(e=>e.getBoundingClientRect().height)})''')
|
||||
if kind=='topology':
|
||||
item=page.locator('[role=treeitem][tabindex="0"]').first;item.focus();page.keyboard.press('End');page.wait_for_function("document.activeElement === [...document.querySelectorAll('[role=treeitem]')].at(-1)")
|
||||
data['endFocus']=page.evaluate('''() => ({role:document.activeElement.getAttribute('role'),bottom:document.activeElement.querySelector('.tv-row')?.getBoundingClientRect().bottom,viewport:innerHeight})''')
|
||||
page.keyboard.press('Home');page.wait_for_function("document.activeElement === document.querySelector('[role=treeitem]')")
|
||||
page.get_by_role('button',name='Load instances',exact=True).first.click()
|
||||
page.get_by_text('Page 1',exact=True).first.wait_for();data['lazyLoad']=True
|
||||
page.get_by_role('textbox',name='Search loaded sites, areas and instances').fill('facility 01');data['loadedSearchQualifier']=page.get_by_role('textbox',name='Search loaded sites, areas and instances').get_attribute('placeholder')
|
||||
else: data['orderPass']=data['sites']==['offline','warning','unknown','healthy']
|
||||
data.update(kind=kind,zoom=zoom,requested=[w,h],errors=errors)
|
||||
data['passed']=data['document']<=data['width'] and data['scheme']==scheme and not errors and len(data['headings'])==1 and (data.get('orderPass',True)) and (kind!='topology' or data['endFocus']['bottom']<=data['endFocus']['viewport'])
|
||||
if zoom==2: page.screenshot(path=str(OUT/f'{kind}-{scheme}-{w}-{h}-zoom{zoom}.png'),full_page=False)
|
||||
else: page.screenshot(path=str(OUT/f'{kind}-{scheme}-{w}-{h}-zoom{zoom}.png'),full_page=False)
|
||||
results.append(data);(OUT/'progress.json').write_text(json.dumps(results,indent=2));page.close()
|
||||
for role,templates,audit in [('Viewer',False,True),('Designer',True,False),('Deployer',False,False),('Administrator,Designer,Deployer',True,True)]:
|
||||
page=context.new_page();page.goto('http://127.0.0.1:5201/batch-two?Sample=dashboard&role='+role)
|
||||
page.get_by_role('region',name='Sites configured',exact=True).wait_for();page.wait_for_timeout(300)
|
||||
data={target:page.locator('main a[href="'+target+'"]').count() for target in ['/design/templates','/audit/configuration']}
|
||||
# Rail uses the same real policies; hidden native navigation remains in DOM for comparison.
|
||||
nav={target:page.locator('nav a[href="'+target+'"]').count() for target in data}
|
||||
results.append(dict(kind='policy',role=role,shortcuts=data,rail=nav,passed=data['/design/templates']==int(templates) and data['/audit/configuration']==int(audit) and data==nav));(OUT/'progress.json').write_text(json.dumps(results,indent=2))
|
||||
page.close()
|
||||
touch_browser=p.chromium.launch(channel='chromium',args=['--touch-events=enabled'])
|
||||
for w,h in [(390,844),(768,480)]:
|
||||
touch=touch_browser.new_context(viewport={'width':w,'height':h},is_mobile=True,has_touch=True)
|
||||
page=touch.new_page();page.goto('http://127.0.0.1:5201/topology-canary?role=Deployer');page.locator('.tv-root').wait_for();page.wait_for_function("matchMedia('(pointer:coarse)').matches")
|
||||
record=page.evaluate("""() => ({width:innerWidth,document:document.documentElement.scrollWidth,coarse:matchMedia('(pointer:coarse)').matches,rows:[...document.querySelectorAll('.tv-row')].slice(0,8).map(e=>e.getBoundingClientRect().height),toggles:[...document.querySelectorAll('.tv-toggle')].slice(0,8).map(e=>({width:e.getBoundingClientRect().width,height:e.getBoundingClientRect().height})),overflow:getComputedStyle(document.querySelector('.topology-tree')).overflowY})""")
|
||||
record.update(kind='touch',requested=[w,h]);record['passed']=record['coarse'] and min(record['rows'])>=44 and all(t['width']>=44 and t['height']>=44 for t in record['toggles']) and record['document']<=record['width'] and record['overflow']=='visible'
|
||||
results.append(record);page.screenshot(path=str(OUT/f'topology-touch-{w}-{h}.png'));touch.close()
|
||||
touch_browser.close()
|
||||
(OUT/'results.json').write_text(json.dumps(results,indent=2));print(json.dumps({'cases':len(results),'passed':sum(x['passed'] for x in results),'failures':[r for r in results if not r['passed']]},indent=2))
|
||||
main()
|
||||
Reference in New Issue
Block a user