mirror of
https://github.com/CloakHQ/CloakBrowser.git
synced 2026-06-23 11:41:46 +02:00
fix(wrapper): track real window geometry on headed launches
Headed launches applied a fixed emulated viewport on top of the real browser window, yielding outerWidth < innerWidth (an impossible window). Default headed new_page()/new_context() to no_viewport so the page tracks the real window; headless keeps a deterministic viewport. Covers Python launch/launch_context/launch_persistent_context (+async) and the JS Playwright/Puppeteer wrappers. Explicit viewport still honored.
This commit is contained in:
@@ -33,6 +33,82 @@ def test_default_viewport(mock_launch, _mock_bin):
|
||||
assert ctx_kwargs[1]["viewport"] == DEFAULT_VIEWPORT
|
||||
|
||||
|
||||
@patch("cloakbrowser.browser.ensure_binary", return_value="/fake/chrome")
|
||||
@patch("cloakbrowser.browser.launch")
|
||||
def test_headed_no_viewport(mock_launch, _mock_bin):
|
||||
"""Headed (headless=False): no emulated viewport — no_viewport=True so the page
|
||||
tracks the real window (CDP viewport emulation would force outerWidth < innerWidth)."""
|
||||
browser, context = _make_mock_browser()
|
||||
mock_launch.return_value = browser
|
||||
|
||||
from cloakbrowser.browser import launch_context
|
||||
launch_context(headless=False)
|
||||
|
||||
ctx_kwargs = browser.new_context.call_args[1]
|
||||
assert ctx_kwargs.get("no_viewport") is True
|
||||
assert "viewport" not in ctx_kwargs
|
||||
|
||||
|
||||
def test_default_no_viewport_helper():
|
||||
"""_default_no_viewport defaults new_page()/new_context() to no_viewport=True,
|
||||
but never overrides an explicit viewport (Playwright rejects passing both)."""
|
||||
from cloakbrowser.browser import _default_no_viewport
|
||||
|
||||
browser = MagicMock()
|
||||
orig_new_page = browser.new_page
|
||||
orig_new_context = browser.new_context
|
||||
_default_no_viewport(browser)
|
||||
|
||||
browser.new_page()
|
||||
orig_new_page.assert_called_once_with(no_viewport=True)
|
||||
browser.new_context()
|
||||
orig_new_context.assert_called_once_with(no_viewport=True)
|
||||
|
||||
# Explicit viewport respected — no_viewport NOT injected.
|
||||
orig_new_page.reset_mock()
|
||||
browser.new_page(viewport={"width": 800, "height": 600})
|
||||
orig_new_page.assert_called_once_with(viewport={"width": 800, "height": 600})
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_default_no_viewport_helper_async():
|
||||
"""_default_no_viewport_async mirrors the sync helper for async new_page/new_context."""
|
||||
from cloakbrowser.browser import _default_no_viewport_async
|
||||
|
||||
browser = MagicMock()
|
||||
browser.new_page = AsyncMock()
|
||||
browser.new_context = AsyncMock()
|
||||
orig_new_page = browser.new_page
|
||||
orig_new_context = browser.new_context
|
||||
_default_no_viewport_async(browser)
|
||||
|
||||
await browser.new_page()
|
||||
orig_new_page.assert_awaited_once_with(no_viewport=True)
|
||||
await browser.new_context()
|
||||
orig_new_context.assert_awaited_once_with(no_viewport=True)
|
||||
|
||||
# Explicit viewport respected — no_viewport NOT injected.
|
||||
orig_new_page.reset_mock()
|
||||
await browser.new_page(viewport={"width": 800, "height": 600})
|
||||
orig_new_page.assert_awaited_once_with(viewport={"width": 800, "height": 600})
|
||||
|
||||
|
||||
@patch("cloakbrowser.browser.ensure_binary", return_value="/fake/chrome")
|
||||
@patch("cloakbrowser.browser.launch")
|
||||
def test_conflicting_viewport_kwargs_deduped(mock_launch, _mock_bin):
|
||||
"""If a caller forces no_viewport via **kwargs alongside viewport=, only one
|
||||
reaches Playwright (which rejects both). The explicit kwargs value wins."""
|
||||
browser, context = _make_mock_browser()
|
||||
mock_launch.return_value = browser
|
||||
|
||||
from cloakbrowser.browser import launch_context
|
||||
launch_context(viewport={"width": 1280, "height": 800}, no_viewport=True)
|
||||
|
||||
ctx_kwargs = browser.new_context.call_args[1]
|
||||
assert ctx_kwargs.get("no_viewport") is True
|
||||
assert "viewport" not in ctx_kwargs
|
||||
|
||||
|
||||
@patch("cloakbrowser.browser.ensure_binary", return_value="/fake/chrome")
|
||||
@patch("cloakbrowser.browser.launch")
|
||||
def test_custom_viewport(mock_launch, _mock_bin):
|
||||
|
||||
@@ -55,6 +55,22 @@ def test_persistent_context_default_viewport(_mock_geoip, _mock_bin):
|
||||
assert call_kwargs["viewport"] == DEFAULT_VIEWPORT
|
||||
|
||||
|
||||
@patch("cloakbrowser.browser.ensure_binary", return_value="/fake/chrome")
|
||||
@patch("cloakbrowser.browser.maybe_resolve_geoip", return_value=(None, None, None))
|
||||
def test_persistent_context_headed_no_viewport(_mock_geoip, _mock_bin):
|
||||
"""Headed (headless=False): no_viewport=True instead of DEFAULT_VIEWPORT so the
|
||||
page tracks the real window (avoids the outerWidth < innerWidth tell)."""
|
||||
pw_cm, pw, context = _make_mock_pw_and_context()
|
||||
|
||||
with patch("playwright.sync_api.sync_playwright", return_value=pw_cm):
|
||||
from cloakbrowser.browser import launch_persistent_context
|
||||
launch_persistent_context("/tmp/profile", headless=False)
|
||||
|
||||
call_kwargs = pw.chromium.launch_persistent_context.call_args[1]
|
||||
assert call_kwargs.get("no_viewport") is True
|
||||
assert "viewport" not in call_kwargs
|
||||
|
||||
|
||||
@patch("cloakbrowser.browser.ensure_binary", return_value="/fake/chrome")
|
||||
@patch("cloakbrowser.browser.maybe_resolve_geoip", return_value=(None, None, None))
|
||||
def test_persistent_context_custom_viewport(_mock_geoip, _mock_bin):
|
||||
|
||||
Reference in New Issue
Block a user