From 957f84d9a56fced695aa01fbdc93ff742634143e Mon Sep 17 00:00:00 2001 From: chrissbaumann Date: Thu, 14 May 2026 11:21:10 +0200 Subject: [PATCH] test: add pytest markers, conftest, fix Windows-incompatible existing tests - Add tests/conftest.py with deterministic_rng + sample_profile fixtures - Register unit/integration/e2e markers in pyproject.toml - Mark existing 14 tests as @pytest.mark.unit - Fix test_cli.py: use 'invisible_playwright' (underscore) for 'python -m' - Fix test_translate_includes_gpu_renderer: assert Windows behavior (empty renderer) --- pyproject.toml | 7 +++++++ tests/conftest.py | 17 +++++++++++++++++ tests/test_cli.py | 10 +++++++--- tests/test_constants.py | 10 ++++++++-- tests/test_download.py | 4 +++- tests/test_prefs.py | 14 +++++++++++--- 6 files changed, 53 insertions(+), 9 deletions(-) create mode 100644 tests/conftest.py diff --git a/pyproject.toml b/pyproject.toml index 8c0b3db..01ad3c3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,3 +43,10 @@ packages = ["src/invisible_playwright"] [tool.hatch.build.targets.wheel.force-include] "src/invisible_playwright/data" = "invisible_playwright/data" "src/invisible_playwright/_fpforge/data" = "invisible_playwright/_fpforge/data" + +[tool.pytest.ini_options] +markers = [ + "unit: pure-logic tests, no I/O or external deps", + "integration: multi-module tests, no browser", + "e2e: requires patched Firefox binary and display", +] diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..429aa6d --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,17 @@ +import random + +import pytest + +from invisible_playwright._fpforge import generate_profile + + +@pytest.fixture +def deterministic_rng(): + """Seeded RNG for reproducible tests.""" + return random.Random(42) + + +@pytest.fixture +def sample_profile(): + """A Profile generated from seed=42 for reuse across tests.""" + return generate_profile(seed=42) diff --git a/tests/test_cli.py b/tests/test_cli.py index d52712c..b4d09a6 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,19 +1,23 @@ import subprocess import sys +import pytest + +@pytest.mark.unit def test_version_subcommand(): r = subprocess.run( - [sys.executable, "-m", "invisible-playwright", "version"], + [sys.executable, "-m", "invisible_playwright", "version"], capture_output=True, text=True, check=True, ) assert "firefox-" in r.stdout - assert "invisible-playwright" in r.stdout.lower() + assert "invisible_playwright" in r.stdout.lower() +@pytest.mark.unit def test_help_subcommand(): r = subprocess.run( - [sys.executable, "-m", "invisible-playwright", "--help"], + [sys.executable, "-m", "invisible_playwright", "--help"], capture_output=True, text=True, ) assert r.returncode == 0 diff --git a/tests/test_constants.py b/tests/test_constants.py index 8948f84..fcdeed9 100644 --- a/tests/test_constants.py +++ b/tests/test_constants.py @@ -1,29 +1,35 @@ -from invisible_playwright.constants import BINARY_VERSION, BINARY_BASENAME, ARCHIVE_NAME +import pytest + +from invisible_playwright.constants import ARCHIVE_NAME, BINARY_BASENAME, BINARY_VERSION +@pytest.mark.unit def test_binary_version_format(): assert BINARY_VERSION.startswith("firefox-") assert BINARY_VERSION.split("-", 1)[1].isdigit() +@pytest.mark.unit def test_archive_name_windows(): name = ARCHIVE_NAME("win32", "AMD64") assert name.endswith(".zip") assert "win-x86_64" in name +@pytest.mark.unit def test_archive_name_linux(): name = ARCHIVE_NAME("linux", "x86_64") assert name.endswith(".tar.gz") assert "linux-x86_64" in name +@pytest.mark.unit def test_archive_name_unsupported_raises(): - import pytest with pytest.raises(NotImplementedError): ARCHIVE_NAME("darwin", "arm64") +@pytest.mark.unit def test_binary_basename_format(): assert "firefox" in BINARY_BASENAME.lower() assert "stealth" in BINARY_BASENAME.lower() diff --git a/tests/test_download.py b/tests/test_download.py index 8381356..04df648 100644 --- a/tests/test_download.py +++ b/tests/test_download.py @@ -4,8 +4,8 @@ from pathlib import Path import pytest import responses -from invisible_playwright.download import ensure_binary from invisible_playwright.constants import BINARY_VERSION +from invisible_playwright.download import ensure_binary def _make_zip(path: Path, inner_name: str, payload: bytes) -> bytes: @@ -19,6 +19,7 @@ def _make_zip(path: Path, inner_name: str, payload: bytes) -> bytes: return data +@pytest.mark.unit @responses.activate def test_ensure_binary_downloads_and_verifies(tmp_path, monkeypatch): """Full path: cache miss -> HTTP GET -> SHA256 check -> extract -> return path.""" @@ -48,6 +49,7 @@ def test_ensure_binary_downloads_and_verifies(tmp_path, monkeypatch): assert Path(path).name == "firefox.exe" +@pytest.mark.unit @responses.activate def test_ensure_binary_rejects_sha_mismatch(tmp_path, monkeypatch): cache = tmp_path / "cache" diff --git a/tests/test_prefs.py b/tests/test_prefs.py index 979d2a3..a4f7857 100644 --- a/tests/test_prefs.py +++ b/tests/test_prefs.py @@ -1,14 +1,19 @@ +import pytest + from invisible_playwright._fpforge import generate_profile from invisible_playwright.prefs import translate_profile_to_prefs -def test_translate_includes_gpu_renderer(): +@pytest.mark.unit +def test_translate_includes_gpu_renderer_windows(): + """On Windows, renderer/vendor are cleared so ANGLE reports native hardware.""" p = generate_profile(seed=42) prefs = translate_profile_to_prefs(p) - assert prefs["zoom.stealth.webgl.renderer"] == p.gpu.renderer - assert prefs["zoom.stealth.webgl.vendor"] == p.gpu.vendor + assert prefs["zoom.stealth.webgl.renderer"] == "" + assert prefs["zoom.stealth.webgl.vendor"] == "" +@pytest.mark.unit def test_translate_includes_screen(): p = generate_profile(seed=42) prefs = translate_profile_to_prefs(p) @@ -16,18 +21,21 @@ def test_translate_includes_screen(): assert prefs["zoom.stealth.screen.height"] == p.screen.height +@pytest.mark.unit def test_translate_is_deterministic_per_seed(): a = translate_profile_to_prefs(generate_profile(seed=42)) b = translate_profile_to_prefs(generate_profile(seed=42)) assert a == b +@pytest.mark.unit def test_translate_varies_across_seeds(): a = translate_profile_to_prefs(generate_profile(seed=1)) b = translate_profile_to_prefs(generate_profile(seed=2)) assert a != b +@pytest.mark.unit def test_translate_has_stealth_baseline_constants(): p = generate_profile(seed=42) prefs = translate_profile_to_prefs(p)