"""Fixtures for the browser end-to-end suite. The capture flags matter: with a fake UI and a fake device, Chromium answers `getDisplayMedia()` with a real, *moving* 1280x720 monitor stream. That means these tests exercise the genuine capture -> encode -> SRTP -> decode -> paint path rather than a stubbed-out media source. """ from __future__ import annotations import os import socket import subprocess import sys import time from pathlib import Path import pytest from playwright.sync_api import expect, sync_playwright pytestmark = pytest.mark.e2e REPO_ROOT = Path(__file__).resolve().parents[2] CHROMIUM_ARGS = [ "--use-fake-ui-for-media-stream", "--use-fake-device-for-media-stream", "--use-fake-device-for-audio-capture", "--autoplay-policy=no-user-gesture-required", # Two Chromium instances must each get their own UDP ports; never proxy. "--no-sandbox", "--disable-dev-shm-usage", "--no-proxy-server", ] def _free_port() -> int: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as probe: probe.bind(("127.0.0.1", 0)) return probe.getsockname()[1] @pytest.fixture(scope="session") def base_url() -> str: """A real `mirror serve` process on a real port — not a test double.""" import httpx port = _free_port() process = subprocess.Popen( [ sys.executable, "-m", "mirror", "serve", "--host", "127.0.0.1", "--port", str(port), "--log-level", "warning", ], cwd=REPO_ROOT, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, env={ **os.environ, "MIRROR_ICE_SERVERS": "", "PYTHONUNBUFFERED": "1", # A stray DEBUGPY in the developer's shell would stall the server. "DEBUGPY": "0", }, ) url = f"http://127.0.0.1:{port}" deadline = time.time() + 25 last_error: Exception | None = None while time.time() < deadline: if process.poll() is not None: output = process.stdout.read() if process.stdout else "" raise RuntimeError(f"mirror serve exited early ({process.returncode}):\n{output}") try: if httpx.get(f"{url}/healthz", timeout=1.0).status_code == 200: break except Exception as exc: # still booting last_error = exc time.sleep(0.15) else: process.terminate() raise RuntimeError(f"mirror serve never came up: {last_error}") yield url process.terminate() try: process.wait(timeout=5) except subprocess.TimeoutExpired: # pragma: no cover process.kill() @pytest.fixture(scope="session") def browser(): with sync_playwright() as playwright: # channel="chromium" = the full Chromium binary in new headless mode. # The trimmed "headless shell" has no media stack at all and hides # navigator.mediaDevices entirely. instance = playwright.chromium.launch(channel="chromium", headless=True, args=CHROMIUM_ARGS) yield instance instance.close() @pytest.fixture def context(browser): """One browser context == one device. Closed after every test.""" created = browser.new_context(viewport={"width": 1100, "height": 720}) yield created created.close() @pytest.fixture def page(context): return context.new_page() def start_sharing(page) -> None: page.locator("#start").click() expect(page.locator("body")).to_have_attribute("data-live", "on", timeout=15_000) def stop_sharing(page) -> None: page.locator("#stop").click() expect(page.locator("body")).to_have_attribute("data-live", "off", timeout=10_000) # Returns {width, height, currentTime, distinct} for the first