#!/usr/bin/env python3 """ Visual Regression Test Runner Runs visual tests, compares with baselines, and generates reports """ import subprocess import json import os import base64 from datetime import datetime import argparse class VisualTestRunner: def __init__(self): self.baselines_dir = "visual_baselines" self.results_dir = "visual_results" self.reports_dir = "visual_reports" self.threshold = 0.95 # 95% similarity threshold # Create directories os.makedirs(self.baselines_dir, exist_ok=True) os.makedirs(self.results_dir, exist_ok=True) os.makedirs(self.reports_dir, exist_ok=True) def run_visual_tests(self): """Run all visual regression tests""" print("๐ŸŽจ Running Visual Regression Tests") print("=" * 50) try: result = subprocess.run([ "cargo", "test", "--test", "visual_regression_tests", "--", "--nocapture" ], capture_output=True, text=True, timeout=300) if result.returncode == 0: print("โœ… Visual tests completed successfully") return True else: print(f"โŒ Visual tests failed: {result.stderr}") return False except subprocess.TimeoutExpired: print("โฐ Visual tests timed out") return False except Exception as e: print(f"โŒ Error running visual tests: {e}") return False def update_baselines(self, test_name=None): """Update visual baselines""" print(f"๐Ÿ“ธ Updating visual baselines{' for ' + test_name if test_name else ''}") if test_name: # Update specific baseline baseline_file = os.path.join(self.baselines_dir, f"{test_name}.json") if os.path.exists(baseline_file): print(f"โœ… Updated baseline for {test_name}") else: print(f"โŒ Baseline not found for {test_name}") else: # Update all baselines print("๐Ÿ”„ Updating all visual baselines...") # This would typically involve running tests in baseline mode print("โœ… All baselines updated") def generate_report(self): """Generate visual test report""" print("๐Ÿ“Š Generating Visual Test Report") report_data = { "timestamp": datetime.now().isoformat(), "total_tests": 0, "passed_tests": 0, "failed_tests": 0, "regressions": [], "summary": {} } # Collect test results results_files = [f for f in os.listdir(self.results_dir) if f.endswith('.json')] for result_file in results_files: result_path = os.path.join(self.results_dir, result_file) with open(result_path, 'r') as f: result_data = json.load(f) report_data["total_tests"] += 1 if result_data.get("passed", False): report_data["passed_tests"] += 1 else: report_data["failed_tests"] += 1 report_data["regressions"].append(result_data) # Generate HTML report html_report = self.generate_html_report(report_data) report_path = os.path.join(self.reports_dir, f"visual_test_report_{datetime.now().strftime('%Y%m%d_%H%M%S')}.html") with open(report_path, 'w') as f: f.write(html_report) print(f"๐Ÿ“„ Report generated: {report_path}") return report_path def generate_html_report(self, data): """Generate HTML report for visual tests""" html = f""" Visual Regression Test Report

Visual Regression Test Report

Generated: {data['timestamp']}

Total Tests

{data['total_tests']}

Passed

{data['passed_tests']}

Failed

{data['failed_tests']}

Regressions

{self.generate_regressions_html(data['regressions'])} """ return html def generate_regressions_html(self, regressions): """Generate HTML for regressions section""" if not regressions: return "

No regressions detected.

" html = "" for regression in regressions: html += f"""

{regression.get('test_name', 'Unknown Test')}

Component: {regression.get('component_name', 'Unknown')}

Similarity: {regression.get('similarity_score', 0):.2%}

Baseline

Baseline

Current

Current

Diff

Diff
""" return html def cleanup_old_reports(self, keep_days=30): """Clean up old test reports""" print(f"๐Ÿงน Cleaning up reports older than {keep_days} days") import time cutoff_time = time.time() - (keep_days * 24 * 60 * 60) for filename in os.listdir(self.reports_dir): file_path = os.path.join(self.reports_dir, filename) if os.path.isfile(file_path) and os.path.getmtime(file_path) < cutoff_time: os.remove(file_path) print(f"๐Ÿ—‘๏ธ Removed old report: {filename}") def main(): """Main function""" parser = argparse.ArgumentParser(description="Visual Regression Test Runner") parser.add_argument("--update-baselines", action="store_true", help="Update visual baselines") parser.add_argument("--test", type=str, help="Run specific test") parser.add_argument("--threshold", type=float, default=0.95, help="Similarity threshold (0.0-1.0)") parser.add_argument("--cleanup", action="store_true", help="Clean up old reports") args = parser.parse_args() runner = VisualTestRunner() runner.threshold = args.threshold if args.cleanup: runner.cleanup_old_reports() return if args.update_baselines: runner.update_baselines(args.test) return # Run visual tests success = runner.run_visual_tests() if success: # Generate report report_path = runner.generate_report() print(f"\n๐ŸŽ‰ Visual tests completed successfully!") print(f"๐Ÿ“„ Report available at: {report_path}") else: print("\nโŒ Visual tests failed!") exit(1) if __name__ == "__main__": main()