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)
This commit is contained in:
@@ -43,3 +43,10 @@ packages = ["src/invisible_playwright"]
|
|||||||
[tool.hatch.build.targets.wheel.force-include]
|
[tool.hatch.build.targets.wheel.force-include]
|
||||||
"src/invisible_playwright/data" = "invisible_playwright/data"
|
"src/invisible_playwright/data" = "invisible_playwright/data"
|
||||||
"src/invisible_playwright/_fpforge/data" = "invisible_playwright/_fpforge/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",
|
||||||
|
]
|
||||||
|
|||||||
@@ -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)
|
||||||
+7
-3
@@ -1,19 +1,23 @@
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
def test_version_subcommand():
|
def test_version_subcommand():
|
||||||
r = subprocess.run(
|
r = subprocess.run(
|
||||||
[sys.executable, "-m", "invisible-playwright", "version"],
|
[sys.executable, "-m", "invisible_playwright", "version"],
|
||||||
capture_output=True, text=True, check=True,
|
capture_output=True, text=True, check=True,
|
||||||
)
|
)
|
||||||
assert "firefox-" in r.stdout
|
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():
|
def test_help_subcommand():
|
||||||
r = subprocess.run(
|
r = subprocess.run(
|
||||||
[sys.executable, "-m", "invisible-playwright", "--help"],
|
[sys.executable, "-m", "invisible_playwright", "--help"],
|
||||||
capture_output=True, text=True,
|
capture_output=True, text=True,
|
||||||
)
|
)
|
||||||
assert r.returncode == 0
|
assert r.returncode == 0
|
||||||
|
|||||||
@@ -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():
|
def test_binary_version_format():
|
||||||
assert BINARY_VERSION.startswith("firefox-")
|
assert BINARY_VERSION.startswith("firefox-")
|
||||||
assert BINARY_VERSION.split("-", 1)[1].isdigit()
|
assert BINARY_VERSION.split("-", 1)[1].isdigit()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
def test_archive_name_windows():
|
def test_archive_name_windows():
|
||||||
name = ARCHIVE_NAME("win32", "AMD64")
|
name = ARCHIVE_NAME("win32", "AMD64")
|
||||||
assert name.endswith(".zip")
|
assert name.endswith(".zip")
|
||||||
assert "win-x86_64" in name
|
assert "win-x86_64" in name
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
def test_archive_name_linux():
|
def test_archive_name_linux():
|
||||||
name = ARCHIVE_NAME("linux", "x86_64")
|
name = ARCHIVE_NAME("linux", "x86_64")
|
||||||
assert name.endswith(".tar.gz")
|
assert name.endswith(".tar.gz")
|
||||||
assert "linux-x86_64" in name
|
assert "linux-x86_64" in name
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
def test_archive_name_unsupported_raises():
|
def test_archive_name_unsupported_raises():
|
||||||
import pytest
|
|
||||||
with pytest.raises(NotImplementedError):
|
with pytest.raises(NotImplementedError):
|
||||||
ARCHIVE_NAME("darwin", "arm64")
|
ARCHIVE_NAME("darwin", "arm64")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
def test_binary_basename_format():
|
def test_binary_basename_format():
|
||||||
assert "firefox" in BINARY_BASENAME.lower()
|
assert "firefox" in BINARY_BASENAME.lower()
|
||||||
assert "stealth" in BINARY_BASENAME.lower()
|
assert "stealth" in BINARY_BASENAME.lower()
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ from pathlib import Path
|
|||||||
import pytest
|
import pytest
|
||||||
import responses
|
import responses
|
||||||
|
|
||||||
from invisible_playwright.download import ensure_binary
|
|
||||||
from invisible_playwright.constants import BINARY_VERSION
|
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:
|
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
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
@responses.activate
|
@responses.activate
|
||||||
def test_ensure_binary_downloads_and_verifies(tmp_path, monkeypatch):
|
def test_ensure_binary_downloads_and_verifies(tmp_path, monkeypatch):
|
||||||
"""Full path: cache miss -> HTTP GET -> SHA256 check -> extract -> return path."""
|
"""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"
|
assert Path(path).name == "firefox.exe"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
@responses.activate
|
@responses.activate
|
||||||
def test_ensure_binary_rejects_sha_mismatch(tmp_path, monkeypatch):
|
def test_ensure_binary_rejects_sha_mismatch(tmp_path, monkeypatch):
|
||||||
cache = tmp_path / "cache"
|
cache = tmp_path / "cache"
|
||||||
|
|||||||
+11
-3
@@ -1,14 +1,19 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
from invisible_playwright._fpforge import generate_profile
|
from invisible_playwright._fpforge import generate_profile
|
||||||
from invisible_playwright.prefs import translate_profile_to_prefs
|
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)
|
p = generate_profile(seed=42)
|
||||||
prefs = translate_profile_to_prefs(p)
|
prefs = translate_profile_to_prefs(p)
|
||||||
assert prefs["zoom.stealth.webgl.renderer"] == p.gpu.renderer
|
assert prefs["zoom.stealth.webgl.renderer"] == ""
|
||||||
assert prefs["zoom.stealth.webgl.vendor"] == p.gpu.vendor
|
assert prefs["zoom.stealth.webgl.vendor"] == ""
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
def test_translate_includes_screen():
|
def test_translate_includes_screen():
|
||||||
p = generate_profile(seed=42)
|
p = generate_profile(seed=42)
|
||||||
prefs = translate_profile_to_prefs(p)
|
prefs = translate_profile_to_prefs(p)
|
||||||
@@ -16,18 +21,21 @@ def test_translate_includes_screen():
|
|||||||
assert prefs["zoom.stealth.screen.height"] == p.screen.height
|
assert prefs["zoom.stealth.screen.height"] == p.screen.height
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
def test_translate_is_deterministic_per_seed():
|
def test_translate_is_deterministic_per_seed():
|
||||||
a = translate_profile_to_prefs(generate_profile(seed=42))
|
a = translate_profile_to_prefs(generate_profile(seed=42))
|
||||||
b = translate_profile_to_prefs(generate_profile(seed=42))
|
b = translate_profile_to_prefs(generate_profile(seed=42))
|
||||||
assert a == b
|
assert a == b
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
def test_translate_varies_across_seeds():
|
def test_translate_varies_across_seeds():
|
||||||
a = translate_profile_to_prefs(generate_profile(seed=1))
|
a = translate_profile_to_prefs(generate_profile(seed=1))
|
||||||
b = translate_profile_to_prefs(generate_profile(seed=2))
|
b = translate_profile_to_prefs(generate_profile(seed=2))
|
||||||
assert a != b
|
assert a != b
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
def test_translate_has_stealth_baseline_constants():
|
def test_translate_has_stealth_baseline_constants():
|
||||||
p = generate_profile(seed=42)
|
p = generate_profile(seed=42)
|
||||||
prefs = translate_profile_to_prefs(p)
|
prefs = translate_profile_to_prefs(p)
|
||||||
|
|||||||
Reference in New Issue
Block a user