Files
wehub-resource-sync 542cfa195c
CI / Frontend build (push) Failing after 9m6s
CI / Plugin validate (push) Failing after 9m27s
CI / Python lint (push) Failing after 16m1s
CI / Tests (push) Successful in 18m0s
Deploy / deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:27 +08:00

172 lines
5.8 KiB
Python

"""Tests for the --cdp-url attach-to-existing-browser path.
These run on a core install (no chrome, no browser): they exercise URL
normalization and the routing logic that decides between attaching to a running
browser and launching a throwaway one — without ever opening a browser.
"""
import json
import sys
from pathlib import Path
import pytest
from pixelrag_render.backends import cdp
_BIN = Path(sys.executable).parent
def test_http_base_normalization():
f = cdp._http_base_from_cdp_url
assert f("http://127.0.0.1:9222") == "http://127.0.0.1:9222"
assert f("http://127.0.0.1:9222/json/version") == "http://127.0.0.1:9222"
assert f("127.0.0.1:9222") == "http://127.0.0.1:9222"
assert f("ws://localhost:9222/devtools/browser/abc") == "http://localhost:9222"
def test_cdp_url_routes_to_attach_without_launching(monkeypatch, tmp_path):
"""With cdp_url set, render_urls must take the attach path and never call
_find_chrome (i.e. never try to launch/auto-install a browser)."""
called = {}
async def fake_attached(urls, output_dir, *a, **kw):
called["attached"] = list(urls)
return [Path(output_dir) / "x.png.tiles"]
def boom():
raise AssertionError("_find_chrome must not run on the attach path")
monkeypatch.setattr(cdp, "_run_batch_attached", fake_attached)
monkeypatch.setattr(cdp, "_find_chrome", boom)
out = cdp.render_urls(
["https://example.com"], tmp_path, cdp_url="http://127.0.0.1:9222"
)
assert called["attached"] == ["https://example.com"]
assert out and out[0].name == "x.png.tiles"
def test_env_var_fallback_routes_to_attach(monkeypatch, tmp_path):
called = {}
async def fake_attached(urls, output_dir, *a, **kw):
called["hit"] = True
return []
monkeypatch.setattr(cdp, "_run_batch_attached", fake_attached)
monkeypatch.setattr(cdp, "_find_chrome", lambda: pytest.fail("should not launch"))
monkeypatch.setenv("PIXELSHOT_CDP_URL", "http://127.0.0.1:9222")
cdp.render_urls(["https://example.com"], tmp_path)
assert called.get("hit") is True
def test_default_path_still_resolves_chrome(monkeypatch, tmp_path):
"""No cdp_url (and no env) → the launch path runs find_chrome as before."""
monkeypatch.delenv("PIXELSHOT_CDP_URL", raising=False)
sentinel = RuntimeError("find_chrome reached")
def boom():
raise sentinel
monkeypatch.setattr(cdp, "_find_chrome", boom)
with pytest.raises(RuntimeError, match="find_chrome reached"):
cdp.render_urls(["https://example.com"], tmp_path)
def test_attach_creates_and_closes_only_its_own_target(monkeypatch, tmp_path):
"""The attach path must create its own target, and on teardown close ONLY
that target — never a pre-existing one, and never close/kill the browser.
Mocks the websocket/CDP layer so no browser is needed: a fake ws records
every CDP method sent on the browser-level connection.
"""
browser_methods = [] # (method, params) sent on the browser ws
class FakeBrowserWS:
async def send(self, raw):
msg = json.loads(raw)
browser_methods.append((msg["method"], msg.get("params", {})))
method = msg["method"]
result = {}
if method == "Target.createTarget":
result = {"targetId": "OUR-TARGET-123"}
self._reply = {"id": msg["id"], "result": result}
async def recv(self):
return json.dumps(self._reply)
async def close(self):
pass
class FakePageWS:
async def send(self, raw):
msg = json.loads(raw)
self._reply = {"id": msg["id"], "result": {}}
async def recv(self):
return json.dumps(self._reply)
async def close(self):
pass
async def fake_connect_ws(ws_url):
return FakeBrowserWS() if ws_url == "BROWSER_WS" else FakePageWS()
# Pre-existing target plus the one we create — only ours must be closed.
def fake_fetch_json(url, cdp_url, timeout=5):
if url.endswith("/json/version"):
return {"webSocketDebuggerUrl": "BROWSER_WS"}
return [
{"id": "PREEXISTING-999", "webSocketDebuggerUrl": "ws://other"},
{"id": "OUR-TARGET-123", "webSocketDebuggerUrl": "PAGE_WS"},
]
async def fake_capture_url(*a, **kw):
return 1
monkeypatch.setattr(cdp, "_connect_ws", fake_connect_ws)
monkeypatch.setattr(cdp, "_fetch_json", fake_fetch_json)
monkeypatch.setattr(cdp, "capture_url", fake_capture_url)
out = cdp.render_urls(
["https://example.com"], tmp_path, cdp_url="http://127.0.0.1:9222"
)
assert out and out[0].name.endswith(".png.tiles")
methods = [m for m, _ in browser_methods]
assert "Target.createTarget" in methods
assert "Target.closeTarget" in methods
# Never closed/killed the browser.
assert "Browser.close" not in methods
# closeTarget targeted ONLY our own created target, never the pre-existing one.
closed = [p["targetId"] for m, p in browser_methods if m == "Target.closeTarget"]
assert closed == ["OUR-TARGET-123"]
def test_attach_bad_cdp_url_raises_clean_error(monkeypatch, tmp_path):
"""An unreachable/bad endpoint surfaces a clear RuntimeError, not a raw
URLError/KeyError traceback."""
import urllib.error
def boom(url, timeout=5):
raise urllib.error.URLError("connection refused")
monkeypatch.setattr(cdp.urllib.request, "urlopen", boom)
with pytest.raises(RuntimeError, match="Could not reach CDP endpoint at"):
cdp.render_urls(
["https://example.com"], tmp_path, cdp_url="http://127.0.0.1:9999"
)
def test_cli_help_exposes_cdp_url():
import subprocess
r = subprocess.run(
[str(_BIN / "pixelshot"), "--help"], capture_output=True, text=True
)
assert r.returncode == 0
assert "--cdp-url" in r.stdout