invisible_playwright/tests/test_backcompat.py

85 lines
3.7 KiB
Python
Raw Normal View History

"""Backward-compatibility guard for the invisible_core / invisible_playwright split.
After extracting the pure config into ``invisible_core`` and turning the moved
modules into aliasing shims, EVERY import path that external users (and our own
outreach PRs) rely on must keep working same objects, same behavior. This test
is the contract: if it breaks, we broke someone's install.
"""
import importlib
import pytest
@pytest.mark.unit
def test_top_level_public_api_imports():
import invisible_playwright as ip
for name in [
"InvisiblePlaywright", "ensure_binary", "ensure_geoip_mmdb",
"get_default_stealth_prefs", "get_default_args",
"resolve_session_timezone", "GeoTimezoneError",
"BINARY_VERSION", "FIREFOX_UPSTREAM_VERSION", "__version__",
]:
assert hasattr(ip, name), f"invisible_playwright.{name} missing"
@pytest.mark.unit
def test_submodule_imports_external_users_rely_on():
# These are the exact import shapes used in the wild + in our Skyvern/onyx/etc PRs.
from invisible_playwright._fpforge import generate_profile, Profile # noqa: F401
from invisible_playwright._webgl_personas import forced_gpu_class # noqa: F401
from invisible_playwright.prefs import translate_profile_to_prefs # noqa: F401
from invisible_playwright.download import ensure_binary # noqa: F401
from invisible_playwright.constants import BINARY_VERSION # noqa: F401
from invisible_playwright.config import get_default_stealth_prefs # noqa: F401
@pytest.mark.unit
def test_deep_submodule_and_private_names_still_import():
from invisible_playwright._fpforge.profile import Profile # noqa: F401
from invisible_playwright._fpforge import _sampler # noqa: F401
from invisible_playwright.prefs import _BASELINE # noqa: F401
assert isinstance(_BASELINE, dict)
@pytest.mark.unit
def test_class_identity_is_shared_with_core():
# isinstance must hold: the object returned by generate_profile must be an
# instance of BOTH the invisible_playwright-path Profile and the core Profile
# (they must be the SAME class object, not duplicates).
from invisible_playwright._fpforge import generate_profile
from invisible_playwright._fpforge import Profile as PW_Profile
from invisible_playwright._fpforge.profile import Profile as PW_Profile2
import invisible_core
from invisible_core import Profile as Core_Profile
p = generate_profile(seed=42)
assert isinstance(p, PW_Profile)
assert isinstance(p, PW_Profile2)
assert isinstance(p, Core_Profile)
assert PW_Profile is Core_Profile
assert PW_Profile2 is Core_Profile
@pytest.mark.unit
def test_shim_output_matches_core_output():
from invisible_playwright._fpforge import generate_profile as gp_pw
from invisible_playwright._webgl_personas import forced_gpu_class as fgc_pw
from invisible_playwright.prefs import translate_profile_to_prefs as tp_pw
import invisible_core as ic
a = tp_pw(gp_pw(42, fixed_gpu_class=fgc_pw(42)))
b = ic.translate_profile_to_prefs(ic.generate_profile(42, fixed_gpu_class=ic.forced_gpu_class(42)))
assert a == b
@pytest.mark.unit
def test_core_imports_without_playwright(monkeypatch):
# invisible_core must be importable even if playwright is absent. We simulate
# by asserting the core modules never pulled playwright into sys.modules as a
# side effect of their own import (the wrapper may have, but core must not need it).
import sys
import invisible_core # noqa: F401
# invisible_core itself must not hard-depend on playwright at import time.
core_file = importlib.import_module("invisible_core").__file__
assert "invisible_core" in core_file