109 lines
5.8 KiB
Python
Executable File
109 lines
5.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""WAVE 163 — L99 VISUAL layer: Playwright visual regression"""
|
|
import asyncio,json,datetime
|
|
from playwright.async_api import async_playwright
|
|
|
|
TESTS = [
|
|
{"page":"agents-archi","url":"https://weval-consulting.com/agents-archi.html","wait":25000,
|
|
"checks":{
|
|
"agent_count":{"selector":".ag-card","min":1,"max":99},
|
|
"master_exists":{"selector":".ag-card.master","min":0,"max":99},
|
|
|
|
"_skip_t4":{"script":'(function(){const c=Array.from(document.querySelectorAll(".ag-card"));const m=c.find(x=>x.dataset.tier==="4");if(!m)return 1;const my=m.getBoundingClientRect().y;const minOther=Math.min(...c.filter(x=>x.dataset.tier!=="4").map(x=>x.getBoundingClientRect().y));return my<minOther?0:1;})()',"max":0},"_skip_t3":{"script":'(function(){const c=Array.from(document.querySelectorAll(".ag-card"));const t3=c.filter(x=>x.dataset.tier==="3").map(x=>x.getBoundingClientRect().y);const t2=c.filter(x=>x.dataset.tier==="2").map(x=>x.getBoundingClientRect().y);if(!t3.length||!t2.length)return 1;const avg3=t3.reduce((a,b)=>a+b,0)/t3.length;const avg2=t2.reduce((a,b)=>a+b,0)/t2.length;return avg3>avg2?0:1;})()',"max":0},
|
|
"tier3_reasonable":{"script":'Array.from(document.querySelectorAll(".ag-card")).filter(c=>c.dataset.tier==="3" && c.getBoundingClientRect().y>1100).length',"max":0},
|
|
"legend_above_agents":{"script":'(function(){const l=document.querySelector(".legend,#legend");if(!l)return 0;const ly=l.getBoundingClientRect().y;return Array.from(document.querySelectorAll(".ag-card")).filter(c=>c.getBoundingClientRect().y>ly+20).length;})()',"max":5},
|
|
}},
|
|
{"page":"wevia-meeting-rooms","url":"https://weval-consulting.com/wevia-meeting-rooms.html","wait":4000,
|
|
"checks":{
|
|
"rooms_count":{"script":'typeof RM!=="undefined"?RM.length:0',"min":8,"max":8},
|
|
"ops_panel":{"selector":"#liveOpsPanel","min":1,"max":1},
|
|
"dir_y_position":{"script":'typeof RM!=="undefined"?RM.find(r=>r.id==="dir").y:0',"min":1400,"max":1700},
|
|
}},
|
|
{"page":"enterprise-model","url":"https://weval-consulting.com/enterprise-model.html","wait":10000,
|
|
"checks":{
|
|
"page_loads":{"script":'document.body.innerText.length',"min":15},
|
|
"nav_present":{"script":'(document.body.innerText.match(/Enterprise|Sovereign|Admin/g)||[]).length',"min":3},
|
|
}},
|
|
{"page":"wevia-master","url":"https://weval-consulting.com/wevia-master.html","wait":3000,
|
|
"checks":{
|
|
"chat_input":{"script":'document.querySelectorAll("textarea, input[type=text]").length',"min":1},
|
|
}},
|
|
{"page":"director-center","url":"https://weval-consulting.com/director-center.html","wait":3000,
|
|
"checks":{
|
|
"unified_overlay":{"selector":"#unifiedLiveOverlay","min":1,"max":1},
|
|
}},
|
|
{"page":"l99-brain","url":"https://weval-consulting.com/l99-brain.html","wait":3000,
|
|
"checks":{
|
|
"unified_overlay":{"selector":"#unifiedLiveOverlay","min":1,"max":1},
|
|
}},
|
|
{"page":"paperclip","url":"https://paperclip.weval-consulting.com/","wait":8000,
|
|
"checks":{
|
|
"has_content":{"script":'document.body.innerText.length',"min":15},
|
|
}},
|
|
]
|
|
|
|
async def check_val(page, check):
|
|
if "selector" in check:
|
|
script = 'document.querySelectorAll("'+check["selector"]+'").length'
|
|
else:
|
|
script = check["script"]
|
|
return await page.evaluate(script)
|
|
|
|
async def run_tests():
|
|
results = []
|
|
passed = 0
|
|
failed = 0
|
|
async with async_playwright() as p:
|
|
browser = await p.chromium.launch(headless=True)
|
|
ctx = await browser.new_context(
|
|
storage_state="/opt/weval-l99/sso-state.json",
|
|
viewport={"width":1920,"height":1080}
|
|
)
|
|
page = await ctx.new_page()
|
|
for t in TESTS:
|
|
try:
|
|
await page.goto(t["url"],timeout=60000,wait_until="domcontentloaded")
|
|
await page.wait_for_timeout(t.get("wait",3000))
|
|
# Extra wait for heavy 3D pages
|
|
if t.get("wait",0) >= 10000:
|
|
try:
|
|
await page.wait_for_selector(".ag-card", timeout=10000)
|
|
except:
|
|
pass
|
|
pr = {"page":t["page"],"checks":{},"pass":True}
|
|
for name,chk in t["checks"].items():
|
|
try:
|
|
val = await check_val(page,chk)
|
|
ok = True
|
|
if "min" in chk and val < chk["min"]: ok = False
|
|
if "max" in chk and val > chk["max"]: ok = False
|
|
pr["checks"][name]={"value":val,"ok":ok}
|
|
if ok: passed += 1
|
|
else:
|
|
failed += 1
|
|
pr["pass"] = False
|
|
except Exception as e:
|
|
pr["checks"][name]={"error":str(e)[:100],"ok":False}
|
|
failed += 1
|
|
pr["pass"] = False
|
|
results.append(pr)
|
|
except Exception as e:
|
|
results.append({"page":t["page"],"error":str(e)[:200],"pass":False})
|
|
failed += 1
|
|
await browser.close()
|
|
return {"passed":passed,"failed":failed,"total":passed+failed,"pages":results,"ts":datetime.datetime.now().isoformat()}
|
|
|
|
if __name__ == "__main__":
|
|
result = asyncio.run(run_tests())
|
|
out = "/opt/weval-l99/l99-visual-state.json"
|
|
with open(out,"w") as f:
|
|
json.dump(result,f,indent=2)
|
|
print("VISUAL: "+str(result["passed"])+"/"+str(result["total"])+" PASS")
|
|
for r in result["pages"]:
|
|
status = "PASS" if r.get("pass") else "FAIL"
|
|
print(" "+status+" "+r["page"])
|
|
if not r.get("pass") and "checks" in r:
|
|
for n,c in r["checks"].items():
|
|
if not c.get("ok"):
|
|
print(" FAIL "+n+": "+str(c))
|