mirror of
https://github.com/dgtlmoon/changedetection.io.git
synced 2026-10-04 11:08:00 +00:00
An "extra browser" was a name + a ws(s):// endpoint in settings.requests.extra_browsers,
selected by a watch as the magic string 'extra_browser_<name>'. That string resolved to
html_webdriver plus a custom connection URL, which meant the protocol the endpoint was
spoken to came from env vars rather than from the entry: CDP over a WebSocket with
PLAYWRIGHT_DRIVER_URL set, CDP via pyppeteer with FAST_PUPPETEER_CHROME_FETCHER, and the
W3C WebDriver protocol over HTTP on a Selenium-only install - where a wss:// URL cannot
work at all. The form only ever accepted ws:// / wss://, so the feature was silently
broken on exactly the installs that could not honour it.
So it becomes an engine, html_external_cdp, which pins the protocol: a subclass of the
Playwright fetcher that takes its endpoint from the watch's browser config
(FetcherConfig.connection_url) instead of the environment. It is base-only
(ready_to_use=False) because an endpoint is required, so each endpoint is one browser
config ("variation") on the Browsers page - which is what the old settings list was.
update_36 migrates each extra_browsers row to such a variation, keyed by the SAME
'extra_browser_<name>' string watches already hold, so no watch, group override, API value
or global default needs rewriting; the legacy selector simply becomes a real browser-config
id. A row whose endpoint the model rejects is logged and skipped rather than taking the
update chain, and with it startup, down.
Knock-on cleanups, all of which delete a special case rather than add one:
- The proxy opt-out for custom endpoints is now Fetcher.ignores_proxy_setting, asked of
the engine, instead of a string-prefix test in call_browser().
- A live browser-steps / visual-selector session asks the engine where to connect
(Fetcher.browser_steps_connection_url, overridden by html_external_cdp) and refuses an
engine whose supports_browser_steps is False, instead of reading the env var itself and
silently stepping a browser the watch does not check with. That refusal is real: on a
Selenium install html_webdriver cannot drive a live session.
- is_valid_browser_selector() answers "may a watch store this in fetch_backend?" in one
place; the API (create/update/import), the quick-add form validator and the bulk "set
browser" operation each had their own copy, which is how they came to disagree about
whether a browser-config id was acceptable.
- api-spec.yaml's fetch_backend pattern enumerated extra_browser_* while rejecting
browser-config ids and every engine newer than html_webdriver. Valid values are
per-install, so the schema now bounds the string and the handlers do the real check.
- html_external_cdp registers unconditionally (unlike html_playwright_builtin): migrated
configs name it, so it must resolve even without the playwright library, or those
watches would quietly fetch with the plain HTTP client. The library is imported lazily
inside run(), and an unavailable engine now warns instead of falling back silently.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
107 lines
4.4 KiB
Python
107 lines
4.4 KiB
Python
#!/usr/bin/env python3
|
|
import os
|
|
|
|
from flask import url_for
|
|
from ..util import live_server_setup, wait_for_all_checks
|
|
|
|
def do_test(client, live_server, make_test_use_extra_browser=False):
|
|
|
|
# Grep for this string in the logs?
|
|
test_url = "https://changedetection.io/ci-test.html?non-custom-default=true"
|
|
# "non-custom-default" should not appear in the custom browser connection
|
|
custom_browser_name = 'custom browser URL'
|
|
|
|
# needs to be set and something like 'ws://127.0.0.1:3000'
|
|
assert os.getenv('PLAYWRIGHT_DRIVER_URL'), "Needs PLAYWRIGHT_DRIVER_URL set for this test"
|
|
|
|
#####################
|
|
res = client.post(
|
|
url_for("settings.settings_page"),
|
|
data={"application-empty_pages_are_a_change": "",
|
|
"requests-time_between_check-minutes": 180,
|
|
},
|
|
follow_redirects=True
|
|
)
|
|
|
|
assert b"Settings updated." in res.data
|
|
|
|
# A second browser, running somewhere else. This used to be a settings.requests.extra_browsers
|
|
# row; it is now an ordinary browser config based on the External CDP engine (update_36
|
|
# migrates the old rows to exactly this). Keyed by the legacy selector so that migrated
|
|
# installs and this test name the browser the same way.
|
|
res = client.post(
|
|
url_for("ui.browser_config.browser_config_add", base_fetcher='html_external_cdp'),
|
|
data={'label': custom_browser_name,
|
|
'connection_url': 'ws://sockpuppetbrowser-custom-url:3000',
|
|
'screenshot_format': 'JPEG'},
|
|
follow_redirects=True
|
|
)
|
|
assert b"Browser added" in res.data
|
|
custom_browser_id = next(c for c, e in
|
|
client.application.config.get('DATASTORE').browser_config_store.all().items()
|
|
if e.get('label') == custom_browser_name)
|
|
|
|
# The global "Default browser" is now chosen on the /browsers tab (was the settings-page
|
|
# application-fetch_backend radio). Make html_webdriver the default.
|
|
res = client.post(
|
|
url_for("ui.browser_config.browser_config_set_default", config_id="html_webdriver"),
|
|
follow_redirects=True
|
|
)
|
|
assert b"Default browser set" in res.data
|
|
|
|
# Add our URL to the import page
|
|
uuid = client.application.config.get('DATASTORE').add_watch(url=test_url)
|
|
client.post(url_for("ui.form_watch_checknow"), follow_redirects=True)
|
|
wait_for_all_checks(client)
|
|
|
|
if make_test_use_extra_browser:
|
|
|
|
# So the name should appear in the edit page under "Request" > "Fetch Method"
|
|
res = client.get(
|
|
url_for("ui.ui_edit.edit_page", uuid="first"),
|
|
follow_redirects=True
|
|
)
|
|
assert b'custom browser URL' in res.data
|
|
|
|
res = client.post(
|
|
url_for("ui.ui_edit.edit_page", uuid="first"),
|
|
data={
|
|
# 'run_customer_browser_url_tests.sh' will search for this string to know if we hit the right browser container or not
|
|
"url": "https://changedetection.io/ci-test.html?custom-browser-search-string=1",
|
|
"tags": "",
|
|
"headers": "",
|
|
'fetch_backend': custom_browser_id,
|
|
'webdriver_js_execute_code': '',
|
|
"time_between_check_use_default": "y"
|
|
},
|
|
follow_redirects=True
|
|
)
|
|
|
|
assert b"Updated watch." in res.data
|
|
wait_for_all_checks(client)
|
|
|
|
# Force recheck
|
|
res = client.post(url_for("ui.form_watch_checknow"), follow_redirects=True)
|
|
assert b'Queued 1 watch for rechecking.' in res.data
|
|
|
|
wait_for_all_checks(client)
|
|
|
|
res = client.get(
|
|
url_for("ui.ui_preview.preview_page", uuid="first"),
|
|
follow_redirects=True
|
|
)
|
|
assert b'cool it works' in res.data
|
|
|
|
|
|
# Requires playwright to be installed
|
|
def test_request_via_custom_browser_url(client, live_server, measure_memory_usage, datastore_path):
|
|
# live_server_setup(live_server) # Setup on conftest per function
|
|
# We do this so we can grep the logs of the custom container and see if the request actually went through that container
|
|
do_test(client, live_server, make_test_use_extra_browser=True)
|
|
|
|
|
|
def test_request_not_via_custom_browser_url(client, live_server, measure_memory_usage, datastore_path):
|
|
# live_server_setup(live_server) # Setup on conftest per function
|
|
# We do this so we can grep the logs of the custom container and see if the request actually went through that container
|
|
do_test(client, live_server, make_test_use_extra_browser=False)
|