mirror of
https://github.com/dgtlmoon/changedetection.io.git
synced 2026-10-06 20:11:46 +00:00
Build and push containers / metadata (push) Canceled after 0s
Build and push containers / build-push-containers (push) Canceled after 0s
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Build distribution 📦 (push) Canceled after 0s
ChangeDetection.io App Test / lint-code (push) Canceled after 0s
ChangeDetection.io App Test / lint-translations (push) Canceled after 0s
ChangeDetection.io App Test / lint-template-i18n (push) Canceled after 0s
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Test the built package works basically. (push) Canceled after 0s
Publish Python 🐍distribution 📦 to PyPI and TestPyPI / Publish Python 🐍 distribution 📦 to PyPI (push) Canceled after 0s
ChangeDetection.io App Test / test-application-3-10 (push) Canceled after 0s
ChangeDetection.io App Test / test-application-3-11 (push) Canceled after 0s
ChangeDetection.io App Test / test-application-3-12 (push) Canceled after 0s
ChangeDetection.io App Test / test-application-3-13 (push) Canceled after 0s
ChangeDetection.io App Test / test-application-3-14 (push) Canceled after 0s
158 lines
5.6 KiB
Python
158 lines
5.6 KiB
Python
import asyncio
|
|
import sys
|
|
from types import ModuleType, SimpleNamespace
|
|
from unittest.mock import AsyncMock, Mock
|
|
|
|
import pytest
|
|
|
|
from changedetectionio.browser_steps.browser_steps import browsersteps_live_ui
|
|
from changedetectionio.content_fetchers.base import get_playwright_bypass_csp
|
|
|
|
|
|
def test_playwright_bypass_csp_defaults_to_enabled(monkeypatch):
|
|
monkeypatch.delenv('PLAYWRIGHT_BYPASS_CSP', raising=False)
|
|
|
|
assert get_playwright_bypass_csp() is True
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
('configured_value', 'expected'),
|
|
[
|
|
('true', True),
|
|
('1', True),
|
|
('yes', True),
|
|
('false', False),
|
|
('0', False),
|
|
('no', False),
|
|
],
|
|
)
|
|
def test_playwright_bypass_csp_parses_boolean_environment_values(
|
|
monkeypatch, configured_value, expected
|
|
):
|
|
monkeypatch.setenv('PLAYWRIGHT_BYPASS_CSP', configured_value)
|
|
|
|
assert get_playwright_bypass_csp() is expected
|
|
|
|
|
|
@pytest.mark.parametrize('configured_value', ('true', 'false'))
|
|
def test_playwright_fetcher_passes_bypass_csp_to_browser_context(monkeypatch, configured_value):
|
|
monkeypatch.setenv('PLAYWRIGHT_BYPASS_CSP', configured_value)
|
|
|
|
class ContextCreationStopped(Exception):
|
|
pass
|
|
|
|
browser = SimpleNamespace(
|
|
new_context=AsyncMock(side_effect=ContextCreationStopped),
|
|
)
|
|
browser_type = SimpleNamespace(
|
|
connect_over_cdp=AsyncMock(return_value=browser),
|
|
)
|
|
|
|
class AsyncPlaywrightContextManager:
|
|
async def __aenter__(self):
|
|
return SimpleNamespace(chromium=browser_type)
|
|
|
|
async def __aexit__(self, exc_type, exc_value, traceback):
|
|
return False
|
|
|
|
async_api_module = ModuleType('playwright.async_api')
|
|
async_api_module.async_playwright = AsyncPlaywrightContextManager
|
|
|
|
errors_module = ModuleType('playwright._impl._errors')
|
|
errors_module.TimeoutError = TimeoutError
|
|
impl_module = ModuleType('playwright._impl')
|
|
impl_module._errors = errors_module
|
|
playwright_module = ModuleType('playwright')
|
|
playwright_module._impl = impl_module
|
|
|
|
monkeypatch.setitem(sys.modules, 'playwright', playwright_module)
|
|
monkeypatch.setitem(sys.modules, 'playwright.async_api', async_api_module)
|
|
monkeypatch.setitem(sys.modules, 'playwright._impl', impl_module)
|
|
monkeypatch.setitem(sys.modules, 'playwright._impl._errors', errors_module)
|
|
|
|
from changedetectionio.content_fetchers.playwright import fetcher
|
|
|
|
with pytest.raises(ContextCreationStopped):
|
|
asyncio.run(fetcher().run(request_headers={}, url='https://example.com'))
|
|
|
|
assert browser.new_context.await_args.kwargs['bypass_csp'] is (configured_value == 'true')
|
|
|
|
|
|
@pytest.mark.parametrize('configured_value', ('true', 'false'))
|
|
def test_browser_steps_passes_bypass_csp_to_browser_context(monkeypatch, configured_value):
|
|
monkeypatch.setenv('PLAYWRIGHT_BYPASS_CSP', configured_value)
|
|
|
|
page = Mock()
|
|
page.wait_for_timeout = AsyncMock()
|
|
context = SimpleNamespace(new_page=AsyncMock(return_value=page))
|
|
browser = SimpleNamespace(new_context=AsyncMock(return_value=context))
|
|
browser_steps = browsersteps_live_ui(
|
|
playwright_browser=browser, start_url='https://example.com'
|
|
)
|
|
|
|
asyncio.run(browser_steps.connect())
|
|
|
|
assert browser.new_context.await_args.kwargs['bypass_csp'] is (configured_value == 'true')
|
|
|
|
|
|
@pytest.mark.parametrize('configured_value', ('true', 'false'))
|
|
def test_puppeteer_only_sends_set_bypass_csp_when_enabled(monkeypatch, configured_value):
|
|
monkeypatch.setenv('PLAYWRIGHT_BYPASS_CSP', configured_value)
|
|
|
|
from changedetectionio.content_fetchers.puppeteer import _configure_puppeteer_csp
|
|
|
|
page = SimpleNamespace(setBypassCSP=AsyncMock())
|
|
asyncio.run(_configure_puppeteer_csp(page))
|
|
|
|
if configured_value == 'true':
|
|
page.setBypassCSP.assert_awaited_once_with(True)
|
|
else:
|
|
page.setBypassCSP.assert_not_awaited()
|
|
|
|
|
|
def test_puppeteer_fetcher_configures_csp_on_created_page(monkeypatch):
|
|
from changedetectionio.content_fetchers import puppeteer
|
|
|
|
class CSPConfigurationReached(Exception):
|
|
pass
|
|
|
|
page = SimpleNamespace(
|
|
evaluate=AsyncMock(return_value='Mozilla/5.0'),
|
|
setUserAgent=AsyncMock(),
|
|
# fetch_page() attaches an 'error' listener before it configures CSP - the crash
|
|
# handling itself is covered by test_puppeteer_page_crash.py
|
|
on=Mock(),
|
|
)
|
|
browser = SimpleNamespace(newPage=AsyncMock(return_value=page))
|
|
pyppeteer_instance = SimpleNamespace(connect=AsyncMock(return_value=browser))
|
|
|
|
pyppeteer_module = ModuleType('pyppeteer')
|
|
pyppeteer_module.Pyppeteer = Mock(return_value=pyppeteer_instance)
|
|
stealth_module = ModuleType('pyppeteerstealth')
|
|
stealth_module.inject_evasions_into_page = AsyncMock()
|
|
monkeypatch.setitem(sys.modules, 'pyppeteer', pyppeteer_module)
|
|
monkeypatch.setitem(sys.modules, 'pyppeteerstealth', stealth_module)
|
|
|
|
configure_csp = AsyncMock(side_effect=CSPConfigurationReached)
|
|
monkeypatch.setattr(puppeteer, '_configure_puppeteer_csp', configure_csp)
|
|
|
|
with pytest.raises(CSPConfigurationReached):
|
|
asyncio.run(
|
|
puppeteer.fetcher().fetch_page(
|
|
current_include_filters=None,
|
|
empty_pages_are_a_change=False,
|
|
fetch_favicon=False,
|
|
ignore_status_codes=False,
|
|
is_binary=False,
|
|
request_body=None,
|
|
request_headers={},
|
|
request_method='GET',
|
|
screenshot_format=None,
|
|
timeout=45,
|
|
url='https://example.com',
|
|
watch_uuid='test-watch',
|
|
)
|
|
)
|
|
|
|
configure_csp.assert_awaited_once_with(page)
|