Deploy to Production / Build & Verify (push) Failing after 5m56s
Ping Search Engines / Notify Search Engines (push) Successful in 2s
Deploy to Production / Pre-Deploy Tests (push) Has been skipped
Deploy to Production / Deploy to Railway (push) Has been skipped
Deploy to Production / Deploy to Render (push) Has been skipped
Deploy to Production / Deploy to VPS (PM2) (push) Has been skipped
Deploy to Production / Deploy to Fly.io (push) Has been skipped
Deploy to Production / Post-Deploy Verification (push) Has been skipped
Deploy to Production / Notify on Failure (push) Successful in 2s
E2E Test Suite / Critical User Journeys (push) Has been skipped
E2E Test Suite / API Integration Tests (push) Has been skipped
E2E Test Suite / Smoke Tests (P0) (push) Failing after 11m26s
E2E Test Suite / Form Interaction Tests (push) Failing after 11m42s
E2E Test Suite / Destructive & Chaos Tests (push) Failing after 12m2s
E2E Test Suite / Cross-Browser Regression (chromium) (push) Failing after 16m14s
E2E Test Suite / Cross-Browser Regression (webkit) (push) Failing after 17m45s
E2E Test Suite / Cross-Browser Regression (firefox) (push) Failing after 25m23s
E2E Test Suite / Security Header Tests (push) Failing after 7m55s
E2E Test Suite / Test Report Summary (push) Failing after 20s
E2E Test Suite / Mobile Device Tests (push) Failing after 2h49m9s
Uptime Monitor / Health & Response Time (push) Failing after 2s
Uptime Monitor / SSL Certificate (push) Successful in 2s
Uptime Monitor / Send Alerts (push) Failing after 3s
Uptime Monitor / Record Uptime Success (push) Has been skipped
155 lines
4.6 KiB
Python
155 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Lighthouse Performance Audit Script
|
|
Runs Lighthouse audits on specified URLs and reports results
|
|
"""
|
|
|
|
import subprocess
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
def run_lighthouse(url, output_file=None):
|
|
"""Run Lighthouse audit on a URL"""
|
|
cmd = [
|
|
'npx', 'lighthouse', url,
|
|
'--output=json',
|
|
'--output-path=stdout',
|
|
'--chrome-flags="--headless --no-sandbox"',
|
|
'--quiet'
|
|
]
|
|
|
|
try:
|
|
result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
|
|
if result.returncode != 0:
|
|
print(f"Error running Lighthouse on {url}: {result.stderr}")
|
|
return None
|
|
|
|
data = json.loads(result.stdout)
|
|
return data
|
|
except subprocess.TimeoutExpired:
|
|
print(f"Timeout running Lighthouse on {url}")
|
|
return None
|
|
except json.JSONDecodeError:
|
|
print(f"Failed to parse Lighthouse output for {url}")
|
|
return None
|
|
except Exception as e:
|
|
print(f"Unexpected error for {url}: {e}")
|
|
return None
|
|
|
|
def extract_scores(lighthouse_data):
|
|
"""Extract key scores from Lighthouse data"""
|
|
if not lighthouse_data or 'categories' not in lighthouse_data:
|
|
return None
|
|
|
|
categories = lighthouse_data['categories']
|
|
return {
|
|
'performance': int(categories.get('performance', {}).get('score', 0) * 100),
|
|
'accessibility': int(categories.get('accessibility', {}).get('score', 0) * 100),
|
|
'best-practices': int(categories.get('best-practices', {}).get('score', 0) * 100),
|
|
'seo': int(categories.get('seo', {}).get('score', 0) * 100),
|
|
}
|
|
|
|
def get_issues(lighthouse_data):
|
|
"""Extract issues from Lighthouse audit"""
|
|
if not lighthouse_data or 'audits' not in lighthouse_data:
|
|
return []
|
|
|
|
issues = []
|
|
audits = lighthouse_data['audits']
|
|
|
|
# Key audits to check
|
|
important_audits = [
|
|
'first-contentful-paint',
|
|
'largest-contentful-paint',
|
|
'cumulative-layout-shift',
|
|
'total-blocking-time',
|
|
'speed-index',
|
|
'interactive',
|
|
'image-alt',
|
|
'document-title',
|
|
'meta-description',
|
|
'link-text',
|
|
'crawlable-anchors',
|
|
'color-contrast',
|
|
'tap-targets',
|
|
'viewport',
|
|
'uses-responsive-images',
|
|
'modern-image-formats',
|
|
'offscreen-images',
|
|
'render-blocking-resources',
|
|
'unminified-css',
|
|
'unminified-javascript',
|
|
'unused-css-rules',
|
|
'unused-javascript',
|
|
]
|
|
|
|
for audit_id in important_audits:
|
|
if audit_id in audits:
|
|
audit = audits[audit_id]
|
|
if audit.get('score') is not None and audit['score'] < 0.9:
|
|
issues.append({
|
|
'id': audit_id,
|
|
'title': audit.get('title', audit_id),
|
|
'score': audit.get('score'),
|
|
'displayValue': audit.get('displayValue', ''),
|
|
'description': audit.get('description', ''),
|
|
})
|
|
|
|
return issues
|
|
|
|
def main():
|
|
if len(sys.argv) < 2:
|
|
print("Usage: python lighthouse_audit.py <url>")
|
|
sys.exit(1)
|
|
|
|
url = sys.argv[1]
|
|
print(f"\n🔍 Running Lighthouse audit on: {url}\n")
|
|
|
|
data = run_lighthouse(url)
|
|
if not data:
|
|
print("❌ Failed to run Lighthouse audit")
|
|
sys.exit(1)
|
|
|
|
scores = extract_scores(data)
|
|
if not scores:
|
|
print("❌ Failed to extract scores")
|
|
sys.exit(1)
|
|
|
|
# Print results
|
|
print("=" * 60)
|
|
print("LIGHTHOUSE SCORES")
|
|
print("=" * 60)
|
|
|
|
for category, score in scores.items():
|
|
emoji = "✅" if score >= 90 else "⚠️" if score >= 50 else "❌"
|
|
print(f"{emoji} {category.upper():20s}: {score:3d}/100")
|
|
|
|
print("\n" + "=" * 60)
|
|
print("ISSUES FOUND")
|
|
print("=" * 60 + "\n")
|
|
|
|
issues = get_issues(data)
|
|
if issues:
|
|
for issue in issues[:10]: # Show top 10 issues
|
|
score_str = f"{int(issue['score'] * 100)}/100" if issue['score'] is not None else "N/A"
|
|
print(f"⚠️ {issue['title']}")
|
|
print(f" Score: {score_str}")
|
|
if issue['displayValue']:
|
|
print(f" Value: {issue['displayValue']}")
|
|
print()
|
|
else:
|
|
print("✅ No major issues found!\n")
|
|
|
|
# Check if all scores >= 90
|
|
all_passed = all(score >= 90 for score in scores.values())
|
|
if all_passed:
|
|
print("🎉 All scores are 90+! Great job!\n")
|
|
sys.exit(0)
|
|
else:
|
|
print("⚠️ Some scores are below 90. Review issues above.\n")
|
|
sys.exit(1)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|