import os import hashlib from PIL import Image, ImageChops import io from browser.screenshot import take _baselines: dict[str, str] = {} # url → baseline screenshot path async def check(url: str) -> dict: """Compare current screenshot with baseline. Returns diff report.""" current_path = await take(url, label="regression") if current_path.startswith("screenshot_error"): return {"check": "visual_regression", "overall": "error", "summary": current_path} if url not in _baselines: _baselines[url] = current_path return { "check": "visual_regression", "overall": "ok", "baseline_set": True, "summary": "✅ Baseline screenshot saved — will compare next run", } baseline_path = _baselines[url] if not os.path.exists(baseline_path): _baselines[url] = current_path return { "check": "visual_regression", "overall": "ok", "summary": "✅ Baseline reset (previous file missing)", } try: img_base = Image.open(baseline_path).convert("RGB") img_current = Image.open(current_path).convert("RGB") # Resize to same dimensions for comparison if img_base.size != img_current.size: img_current = img_current.resize(img_base.size, Image.LANCZOS) diff = ImageChops.difference(img_base, img_current) diff_pixels = sum(sum(row) for row in diff.getdata()) total_pixels= img_base.width * img_base.height * 3 * 255 diff_pct = round((diff_pixels / total_pixels) * 100, 2) if diff_pct > 20: status = "error" summary = f"🚨 Visual change detected: {diff_pct}% of page changed!" elif diff_pct > 5: status = "warning" summary = f"⚠️ Minor visual change: {diff_pct}% of page changed" else: status = "ok" summary = f"✅ No significant visual change ({diff_pct}%)" # Update baseline on major accepted change _baselines[url] = current_path return { "check": "visual_regression", "overall": status, "diff_percent": diff_pct, "baseline_path": baseline_path, "current_path": current_path, "summary": summary, } except Exception as e: return { "check": "visual_regression", "overall": "error", "summary": f"❌ Visual comparison failed: {e}", }