Validate audit windows and isolate dashboard count reads

This commit is contained in:
Joseph Doherty
2026-11-13 18:23:12 -05:00
parent 102cbb2e90
commit 2d30323ae8
55 changed files with 11250 additions and 79 deletions
+20
View File
@@ -0,0 +1,20 @@
"""Bounded local actual-component state/UTC check; never submits a backend write."""
from pathlib import Path
import json
from playwright.sync_api import sync_playwright
out=Path('docs/ui-consistency/evidence/sb04-06/browser-confirmation');out.mkdir(parents=True,exist_ok=True);results=[]
with sync_playwright() as p:
browser=p.chromium.launch(channel='chromium')
for width in [1440,390]:
for zone in ['America/New_York','Asia/Tokyo']:
page=browser.new_page(viewport={'width':width,'height':1000},timezone_id=zone,reduced_motion='reduce');page.goto('http://127.0.0.1:5201/batch-two?sample=audit',wait_until='networkidle');page.select_option('#audit-time-range','Custom');page.fill('#audit-from','2026-03-08T01:30');page.fill('#audit-to','2026-03-08T02:30');page.click('[data-test=filter-apply]');page.wait_for_function('document.querySelector("#local-filter-result").textContent.includes("FromKind=Utc")');text=page.locator('#local-filter-result').inner_text();record={'sample':'audit','width':width,'browserZone':zone,'utcResult':text,'checks':{'literalUTC':'From=2026-03-08T01:30:00.0000000Z' in text and 'To=2026-03-08T02:30:00.0000000Z' in text}}
page.fill('#audit-from','2026-03-08T03:30');page.click('[data-test=filter-apply]');page.locator('#audit-range-error').wait_for();record['checks']['invertedBlocked']=page.locator('#local-filter-result').inner_text()==text and page.get_attribute('#audit-from','aria-invalid')=='true';record['errorColor']=page.locator('#audit-range-error').evaluate('e=>getComputedStyle(e).color');record['checks']['correctionPreserved']=page.input_value('#audit-from')=='2026-03-08T03:30';page.screenshot(path=str(out/f'audit-{zone.split("/")[-1]}-{width}.png'),full_page=True);results.append(record);page.close()
for sample in ['dashboard','toast']:
page=browser.new_page(viewport={'width':width,'height':1000},reduced_motion='reduce');page.goto('http://127.0.0.1:5201/batch-two?sample='+sample,wait_until='networkidle');record={'sample':sample,'width':width,'checks':{}}
if sample=='dashboard':
fact=page.locator('[role=region][aria-label="API keys"]');record['checks']['unavailableNotZero']='Unavailable' in fact.inner_text();page.get_by_role('button',name='Retry API keys',exact=True).click();page.wait_for_function('selector=>{const text=document.querySelector(selector).textContent;return !text.includes("Unavailable")&&!text.includes("Loading");}',arg='[role=region][aria-label="API keys"]');record['checks']['retryKnownZero']='0' in fact.inner_text() and 'Unavailable' not in fact.inner_text();record['checks']['otherFactsRemain']=all('0' in page.locator('[role=region][aria-label="'+label+'"]').inner_text() for label in ['Sites configured','Data connections configured','Templates'])
else:
page.click('#show-toast');page.get_by_role('button',name='Dismiss Local configuration',exact=True).click();page.wait_for_function('document.querySelectorAll(".toast").length===1');record['checks']['onlyTargetDismissed']=page.locator('.toast').count()==1 and 'Local audit query' in page.locator('.toast').inner_text();record['checks']['otherDismissNamed']=page.get_by_role('button',name='Dismiss Local audit query',exact=True).count()==1
record['layout']=page.evaluate('({innerWidth,scrollWidth:document.documentElement.scrollWidth})');page.screenshot(path=str(out/f'{sample}-{width}.png'),full_page=True);results.append(record);page.close()
browser.close()
(out/'results.json').write_text(json.dumps({'results':results,'passed':all(all(r['checks'].values()) for r in results)},indent=2)+'\n');print([(r['sample'],r['width'],all(r['checks'].values())) for r in results])
@@ -0,0 +1,6 @@
"""Actual keyboard partial date validation; no synthetic .value='invalid' workaround."""
from pathlib import Path
import json
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
b=p.chromium.launch(channel='chromium');page=b.new_page(locale='en-US');page.goto('http://127.0.0.1:5201/batch-two?sample=audit',wait_until='networkidle');page.select_option('#audit-time-range','Custom');page.locator('#audit-from').focus();page.keyboard.press('ArrowLeft');page.keyboard.type('1');page.keyboard.press('Tab');before=page.locator('#audit-from').evaluate('e=>({value:e.value,badInput:e.validity.badInput,valid:e.validity.valid})');page.click('[data-test=filter-apply]');page.wait_for_timeout(100);record={'partialInput':before,'afterPartialApply':page.locator('#local-filter-result').inner_text(),'focusedInvalidInput':page.locator('#audit-from').evaluate('e=>e===document.activeElement'),'formCount':page.locator('form[data-test=audit-filter-bar]').count()};cdp=page.context.new_cdp_session(page);doc=cdp.send('DOM.getDocument');node=cdp.send('DOM.querySelector',{'nodeId':doc['root']['nodeId'],'selector':'#audit-from'});ax=cdp.send('Accessibility.getPartialAXTree',{'nodeId':node['nodeId'],'fetchRelatives':False});record['chromiumAXInvalid']=next((x['value']['value'] for x in ax['nodes'][0].get('properties',[]) if x['name']=='invalid'),None);record['explicitAriaInvalid']=page.get_attribute('#audit-from','aria-invalid');page.fill('#audit-from','');page.locator('#audit-to').focus();record['clearedInput']=page.locator('#audit-from').evaluate('e=>({value:e.value,badInput:e.validity.badInput,valid:e.validity.valid})');page.click('[data-test=filter-apply]');page.wait_for_function('document.querySelector("#local-filter-result").textContent.startsWith("From=")');record['afterIntentionalClear']=page.locator('#local-filter-result').inner_text();record['passed']=before['badInput'] and record['afterPartialApply']=='No local filter applied' and record['focusedInvalidInput'] and record['chromiumAXInvalid']=='true' and record['clearedInput']['valid'] and record['afterIntentionalClear']=='From=;To=;FromKind=;ToKind=';Path('docs/ui-consistency/evidence/sb04-06/native-partial-after.json').write_text(json.dumps(record,indent=2)+'\n');print(record);b.close()