File size: 3,747 Bytes
2ecdf7d
5492cc8
2ecdf7d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5492cc8
 
 
 
 
 
2ecdf7d
 
 
 
 
 
 
 
 
 
5492cc8
 
2ecdf7d
 
5492cc8
 
 
 
 
 
 
2ecdf7d
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
import os
import io
import pathlib
import unittest
import urllib.parse

from tests.test_reader_performance import STORE_SCRIPT, static_server, sync_playwright, PlaywrightError

ROOT = pathlib.Path(__file__).parents[1]
SNAPSHOTS = ROOT / "tests/snapshots/reader/chromium-linux"
MATRIX = (("desktop", 1440, 900), ("tablet", 820, 1180), ("mobile", 390, 844))

@unittest.skipIf(sync_playwright is None, "install requirements-test.txt to run Reader visual tests")
class ReaderVisualTests(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.server = static_server(); cls.origin = cls.server.__enter__(); cls.playwright = sync_playwright().start()
        try: cls.browser = cls.playwright.chromium.launch(headless=True)
        except PlaywrightError as error: cls.playwright.stop(); cls.server.__exit__(None, None, None); raise unittest.SkipTest(str(error))

    @classmethod
    def tearDownClass(cls): cls.browser.close(); cls.playwright.stop(); cls.server.__exit__(None, None, None)

    def test_reader_visual_matrix(self):
        if os.environ.get("UPDATE_READER_SNAPSHOTS") != "1" and not all(
            (SNAPSHOTS / f"{name}-{theme}.png").exists()
            for name, _, _ in MATRIX for theme in ("light", "dark")
        ):
            self.skipTest("Reader visual baselines are not installed")
        from PIL import Image, ImageChops
        source = urllib.parse.quote("https://huggingface.co/datasets/VoiceOfML/Test/resolve/main/visual.txt", safe="")
        for name, width, height in MATRIX:
            for theme in ("light", "dark"):
                with self.subTest(viewport=name, theme=theme):
                    context = self.browser.new_context(viewport={"width": width, "height": height}, color_scheme=theme)
                    page = context.new_page(); page.add_init_script(f"localStorage.setItem('theme', '{theme}')")
                    page.route("**/static/reader-store.js", lambda route: route.fulfill(status=200, content_type="text/javascript", body=STORE_SCRIPT))
                    page.route("**/api/reader-content**", lambda route: route.fulfill(status=200, content_type="text/plain", body=("Reader visual fixture\n" * 80).encode()))
                    page.goto(f"{self.origin}/static/reader.html?url={source}&ext=txt&title=Visual", wait_until="domcontentloaded")
                    page.locator(".reader-text").wait_for(); page.add_style_tag(content="*,*::before,*::after{animation:none!important;transition:none!important;caret-color:transparent!important}")
                    page.wait_for_function("() => document.documentElement.dataset.readerPhase === 'ready'")
                    page.evaluate("async () => { await document.fonts.ready; await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r))); }")
                    image = page.screenshot(animations="disabled"); baseline = SNAPSHOTS / f"{name}-{theme}.png"
                    if os.environ.get("UPDATE_READER_SNAPSHOTS") == "1": baseline.parent.mkdir(parents=True, exist_ok=True); baseline.write_bytes(image)
                    self.assertTrue(baseline.exists(), f"missing baseline {baseline}")
                    with Image.open(io.BytesIO(image)) as actual, Image.open(baseline) as expected:
                        self.assertEqual(actual.size, expected.size)
                        diff = ImageChops.difference(actual.convert("RGB"), expected.convert("RGB"))
                        # Chromium occasionally rounds a control-border channel by 1/255.
                        self.assertLessEqual(max(high for _, high in diff.getextrema()), 1)
                        self.assertLessEqual(sum(pixel != (0, 0, 0) for pixel in diff.get_flattened_data()), 8)
                    context.close()