test: activate the full e2e (browser-driving) suite + add fetch --force

The 138 @pytest.mark.e2e tests were doubly inactive: deselected by addopts AND
skipped without a cached binary — and 3 of the 6 per-file firefox_binary
fixtures silently ignored INVPW_BINARY_PATH, so they'd test whatever was cached
even when you pointed the suite elsewhere (a false-confidence trap).

- Centralize firefox_binary into conftest.py (env INVPW_BINARY_PATH → cache →
  skip); delete the 6 duplicates. Unify test_webrtc_realness onto the same env.
- scripts/run_e2e.py: one command that runs ALL e2e against a chosen binary,
  with reruns so an under-load interaction flake (dblclick/hover pass 3/3 in
  isolation) self-heals while a real break fails every attempt. The webrtc e2e
  fake a TCP-only SOCKS locally, so the suite is offline. This is the MANDATORY
  pre-release browser gate (local — hosted runners are too interaction-flaky).
- Running the suite against firefox-9 surfaced a real gap: `invisible_playwright
  fetch --force` was unrecognized (the subparser took no args) though the e2e
  test + docstring expect it. Implement it: drop the cached version dir, refetch.
- Add pytest-rerunfailures + playwright to the dev extras.

Baseline against firefox-9: 136 passed, 1 skipped (linux_only on win host),
1 was the --force gap now fixed.
This commit is contained in:
feder-cr 2026-06-09 15:40:02 +02:00
parent 67b5e7cd5e
commit 5dac302938
11 changed files with 120 additions and 135 deletions

View file

@ -1,8 +1,12 @@
import os
import random
import sys
from pathlib import Path
import pytest
from invisible_playwright._fpforge import generate_profile
from invisible_playwright.constants import BINARY_ENTRY_REL
@pytest.fixture
@ -15,3 +19,36 @@ def deterministic_rng():
def sample_profile():
"""A Profile generated from seed=42 for reuse across tests."""
return generate_profile(seed=42)
@pytest.fixture(scope="session")
def firefox_binary():
"""Locate the patched Firefox binary for E2E tests, or skip cleanly.
Single source of truth for every E2E test (previously each test file had its
own copy and three of them silently ignored INVPW_BINARY_PATH, so they kept
testing whatever was in the cache even when you pointed the suite at a
specific build: a false-confidence trap). Lookup order:
1. ``INVPW_BINARY_PATH`` env var point the whole suite at a local build
or a freshly-extracted release (this is how the full-suite gate runs).
2. Cached binary under ``cache_dir_for_version()`` (post ``fetch``).
3. Skip we never trigger an implicit multi-hundred-MB network download
inside a test run.
"""
env_path = os.environ.get("INVPW_BINARY_PATH")
if env_path:
if Path(env_path).exists():
return env_path
pytest.skip(f"INVPW_BINARY_PATH={env_path!r} does not exist")
if sys.platform not in BINARY_ENTRY_REL:
pytest.skip(f"unsupported platform: {sys.platform}")
from invisible_playwright.download import cache_dir_for_version
entry = cache_dir_for_version() / BINARY_ENTRY_REL[sys.platform]
if not entry.exists():
pytest.skip(
"patched Firefox binary not cached and INVPW_BINARY_PATH unset; "
"set INVPW_BINARY_PATH=<firefox binary> or run `invisible-playwright fetch`"
)
return str(entry)

View file

@ -31,7 +31,6 @@ random free ports.
from __future__ import annotations
import socket
import sys
import threading
from http.server import BaseHTTPRequestHandler, HTTPServer
@ -165,22 +164,6 @@ def cross_origin_harness():
sb.shutdown()
@pytest.fixture(scope="session")
def firefox_binary():
"""Locate the cached patched Firefox binary or skip."""
from invisible_playwright.constants import BINARY_ENTRY_REL
if sys.platform not in BINARY_ENTRY_REL:
pytest.skip(f"unsupported platform: {sys.platform}")
from invisible_playwright.download import cache_dir_for_version
entry = cache_dir_for_version() / BINARY_ENTRY_REL[sys.platform]
if not entry.exists():
pytest.skip(
"patched Firefox binary not cached; run `invisible-playwright fetch` "
"to enable E2E tests"
)
return str(entry)
@pytest.mark.e2e
def test_cross_origin_iframe_url_appears_in_page_frames(firefox_binary, cross_origin_harness):
"""``page.frames`` must list the cross-origin iframe with its real URL.

View file

@ -8,33 +8,9 @@ handling) do not need a binary and always run.
"""
from __future__ import annotations
import sys
import pytest
from invisible_playwright import InvisiblePlaywright
from invisible_playwright.constants import BINARY_ENTRY_REL
@pytest.fixture(scope="session")
def firefox_binary():
"""Locate the patched Firefox binary or skip the calling test.
We do NOT trigger a network download here: ``ensure_binary`` would
pull a multi-hundred-megabyte archive from a private release,
which is not appropriate inside a unit/E2E test run. Instead we
look for an already-cached binary; if missing we skip.
"""
if sys.platform not in BINARY_ENTRY_REL:
pytest.skip(f"unsupported platform: {sys.platform}")
from invisible_playwright.download import cache_dir_for_version
entry = cache_dir_for_version() / BINARY_ENTRY_REL[sys.platform]
if not entry.exists():
pytest.skip(
"patched Firefox binary not cached; run `invisible-playwright fetch` "
"to enable E2E tests"
)
return str(entry)
# ────────────────────────────────────────────────────────────────────

View file

@ -25,12 +25,9 @@ Run only this file:
"""
from __future__ import annotations
import sys
import pytest
from invisible_playwright import InvisiblePlaywright
from invisible_playwright.constants import BINARY_ENTRY_REL
PIN = {
@ -45,29 +42,6 @@ PIN = {
}
@pytest.fixture(scope="session")
def firefox_binary():
"""See test_fingerprint_surface.firefox_binary for the lookup chain."""
import os
env_path = os.environ.get("INVPW_BINARY_PATH")
if env_path:
from pathlib import Path
if Path(env_path).exists():
return env_path
pytest.skip(f"INVPW_BINARY_PATH={env_path!r} does not exist")
if sys.platform not in BINARY_ENTRY_REL:
pytest.skip(f"unsupported platform: {sys.platform}")
from invisible_playwright.download import cache_dir_for_version
entry = cache_dir_for_version() / BINARY_ENTRY_REL[sys.platform]
if not entry.exists():
pytest.skip(
"patched Firefox not cached; run "
"`python -m invisible_playwright fetch` first, or set "
"INVPW_BINARY_PATH to a local build"
)
return str(entry)
@pytest.fixture(scope="module")
def page(firefox_binary):
with InvisiblePlaywright(

View file

@ -27,12 +27,10 @@ Run only this file:
from __future__ import annotations
import re
import sys
import pytest
from invisible_playwright import InvisiblePlaywright
from invisible_playwright.constants import BINARY_ENTRY_REL
# ────────────────────────────────────────────────────────────────────
@ -53,32 +51,6 @@ PIN = {
}
@pytest.fixture(scope="session")
def firefox_binary():
"""Locate the patched Firefox binary. Three lookup paths:
1. ``INVPW_BINARY_PATH`` env var (for dev iteration against a local build)
2. Cached binary under ``cache_dir_for_version()`` (post-fetch)
3. Skip cleanly (no implicit network download)."""
import os
env_path = os.environ.get("INVPW_BINARY_PATH")
if env_path:
from pathlib import Path
if Path(env_path).exists():
return env_path
pytest.skip(f"INVPW_BINARY_PATH={env_path!r} does not exist")
if sys.platform not in BINARY_ENTRY_REL:
pytest.skip(f"unsupported platform: {sys.platform}")
from invisible_playwright.download import cache_dir_for_version
entry = cache_dir_for_version() / BINARY_ENTRY_REL[sys.platform]
if not entry.exists():
pytest.skip(
"patched Firefox not cached; run "
"`python -m invisible_playwright fetch` first, or set "
"INVPW_BINARY_PATH to a local build"
)
return str(entry)
@pytest.fixture(scope="module")
def page(firefox_binary):
"""One headless browser shared across the whole module.

View file

@ -16,24 +16,11 @@ and covers each patched call site:
"""
from __future__ import annotations
import sys
import urllib.parse
import pytest
from invisible_playwright import InvisiblePlaywright
from invisible_playwright.constants import BINARY_ENTRY_REL
@pytest.fixture(scope="session")
def firefox_binary():
if sys.platform not in BINARY_ENTRY_REL:
pytest.skip(f"unsupported platform: {sys.platform}")
from invisible_playwright.download import cache_dir_for_version
entry = cache_dir_for_version() / BINARY_ENTRY_REL[sys.platform]
if not entry.exists():
pytest.skip("patched Firefox binary not cached; run `invisible-playwright fetch`")
return str(entry)
def _data_url(html: str) -> str:

View file

@ -22,35 +22,12 @@ For dev iteration:
from __future__ import annotations
import http.server
import os
import socketserver
import sys
import threading
import pytest
from invisible_playwright import InvisiblePlaywright
from invisible_playwright.constants import BINARY_ENTRY_REL
@pytest.fixture(scope="session")
def firefox_binary():
env_path = os.environ.get("INVPW_BINARY_PATH")
if env_path:
from pathlib import Path
if Path(env_path).exists():
return env_path
pytest.skip(f"INVPW_BINARY_PATH={env_path!r} does not exist")
if sys.platform not in BINARY_ENTRY_REL:
pytest.skip(f"unsupported platform: {sys.platform}")
from invisible_playwright.download import cache_dir_for_version
entry = cache_dir_for_version() / BINARY_ENTRY_REL[sys.platform]
if not entry.exists():
pytest.skip(
"patched Firefox not cached; run `python -m invisible_playwright fetch` "
"or set INVPW_BINARY_PATH"
)
return str(entry)
# ---------------------------------------------------------------------------

View file

@ -342,7 +342,9 @@ _FAKE_EGRESS = "203.0.113.7" # RFC 5737 TEST-NET-3
def _e2e_binary():
cand = os.environ.get("STEALTHFOX_E2E_BINARY")
# Honor both env vars so the whole e2e suite targets one binary from a single
# setting (INVPW_BINARY_PATH is what conftest's firefox_binary uses).
cand = os.environ.get("STEALTHFOX_E2E_BINARY") or os.environ.get("INVPW_BINARY_PATH")
if cand and os.path.exists(cand):
return cand
built = r"C:\ff\source\obj-x86_64-pc-windows-msvc\dist\bin\firefox.exe"