-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_visual.py
More file actions
133 lines (99 loc) · 4.24 KB
/
test_visual.py
File metadata and controls
133 lines (99 loc) · 4.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
"""
Visual regression test for Space Jam 1996 landing page recreation.
Compares rendered page against reference screenshot for pixel-perfect match.
"""
import http.server
import socketserver
import threading
import os
from pathlib import Path
from playwright.sync_api import sync_playwright
from PIL import Image
import numpy as np
REFERENCE_SCREENSHOT = Path(__file__).parent / "screenshot.png"
TEST_SCREENSHOT = Path(__file__).parent / "test_output.png"
DIFF_SCREENSHOT = Path(__file__).parent / "diff_output.png"
HTML_FILE = Path(__file__).parent / "index.html"
PORT = 8765
# Expected dimensions from reference screenshot
EXPECTED_WIDTH = 1791
EXPECTED_HEIGHT = 1000
class ReuseAddrTCPServer(socketserver.TCPServer):
allow_reuse_address = True
def start_server():
"""Start a simple HTTP server to serve the static files."""
os.chdir(Path(__file__).parent)
handler = http.server.SimpleHTTPRequestHandler
httpd = ReuseAddrTCPServer(("", PORT), handler)
thread = threading.Thread(target=httpd.serve_forever, daemon=True)
thread.start()
return httpd
def calculate_pixel_difference(img1_path: Path, img2_path: Path, diff_path: Path) -> tuple[int, float]:
"""
Calculate pixel-by-pixel difference between two images.
Returns (different_pixel_count, percentage_different).
Saves a diff image highlighting differences.
"""
img1 = Image.open(img1_path).convert('RGB')
img2 = Image.open(img2_path).convert('RGB')
# Ensure same size
if img1.size != img2.size:
print(f"Size mismatch: {img1.size} vs {img2.size}")
# Resize img2 to match img1 for comparison
img2 = img2.resize(img1.size, Image.Resampling.LANCZOS)
arr1 = np.array(img1)
arr2 = np.array(img2)
# Calculate difference
diff = np.abs(arr1.astype(int) - arr2.astype(int))
# A pixel is "different" if any channel differs by more than threshold
threshold = 5
diff_mask = np.any(diff > threshold, axis=2)
different_pixels = np.sum(diff_mask)
total_pixels = diff_mask.size
percentage = (different_pixels / total_pixels) * 100
# Create diff image (red highlights differences)
diff_img = np.zeros_like(arr1)
diff_img[diff_mask] = [255, 0, 0] # Red for different pixels
diff_img[~diff_mask] = arr1[~diff_mask] // 2 # Dim unchanged pixels
Image.fromarray(diff_img.astype(np.uint8)).save(diff_path)
return different_pixels, percentage
def test_pixel_perfect_match():
"""Test that the rendered page matches the reference screenshot pixel-perfectly."""
# Check that HTML file exists
if not HTML_FILE.exists():
raise AssertionError(f"index.html not found at {HTML_FILE}. Create the page first!")
# Start local server
server = start_server()
try:
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
# Set viewport to exact screenshot dimensions
page.set_viewport_size({"width": EXPECTED_WIDTH, "height": EXPECTED_HEIGHT})
# Navigate to page
page.goto(f"http://localhost:{PORT}/index.html")
page.wait_for_load_state("networkidle")
# Take screenshot
page.screenshot(path=str(TEST_SCREENSHOT))
print(f"Screenshot saved to {TEST_SCREENSHOT}")
browser.close()
# Compare screenshots
diff_pixels, diff_percentage = calculate_pixel_difference(
REFERENCE_SCREENSHOT, TEST_SCREENSHOT, DIFF_SCREENSHOT
)
print(f"Different pixels: {diff_pixels:,} ({diff_percentage:.2f}%)")
print(f"Diff image saved to {DIFF_SCREENSHOT}")
# For pixel-perfect, we want 0 different pixels (or very close)
# Allow tiny tolerance for anti-aliasing
max_allowed_diff_percentage = 0.1 # 0.1% tolerance
if diff_percentage > max_allowed_diff_percentage:
raise AssertionError(
f"Page does not match reference! "
f"{diff_pixels:,} pixels different ({diff_percentage:.2f}%). "
f"Check {DIFF_SCREENSHOT} for visual diff."
)
print("SUCCESS: Page matches reference screenshot!")
finally:
server.shutdown()
if __name__ == "__main__":
test_pixel_perfect_match()