Files
changedetection.io/changedetectionio/tests/test_link_to_open.py
T
Jeff Hedlund 2f60135cd0 feature: Implemented optional override link (#4290)
* Implemented optional link to override feature

* Added translation strings (untranslated)
2026-09-09 16:06:24 +02:00

172 lines
6.4 KiB
Python

"""
Tests for the optional per-watch "Link to Open" (`link_to_open`).
A watch may point at something that is useless in a browser - an API endpoint or an RSS
feed - while the human-readable page lives somewhere else. `link_to_open` stores that page,
and every "go to the site" affordance (watch list, history/preview header, the
{{watch_open_url}} notification token) uses it in preference to the watched URL.
"""
import os
from flask import url_for
from .util import set_original_response, set_modified_response, wait_for_all_checks, \
wait_for_notification_endpoint_output
OPEN_URL = "https://example.com/the-real-human-page"
def _add_watch(client, test_url):
res = client.post(
url_for("ui.ui_views.form_quick_watch_add"),
data={"url": test_url, "tags": ''},
follow_redirects=True
)
assert b"Watch added" in res.data
wait_for_all_checks(client)
def _edit_watch(client, test_url, **extra):
data = {
"url": test_url,
"tags": "",
"headers": "",
"fetch_backend": "html_requests",
"time_between_check_use_default": "y",
}
data.update(extra)
return client.post(url_for("ui.ui_edit.edit_page", uuid="first"), data=data, follow_redirects=True)
def test_link_to_open_is_saved_and_used_in_the_watch_list(client, live_server, measure_memory_usage, datastore_path):
set_original_response(datastore_path=datastore_path)
test_url = url_for('test_endpoint', _external=True)
_add_watch(client, test_url)
# Without an override, the watch list links to the watched URL
res = client.get(url_for("watchlist.index"))
assert f'href="{test_url}"'.encode('utf-8') in res.data
assert OPEN_URL.encode('utf-8') not in res.data
res = _edit_watch(client, test_url, link_to_open=OPEN_URL)
assert b"Updated watch." in res.data
# It round-trips back into the edit form
res = client.get(url_for("ui.ui_edit.edit_page", uuid="first"))
assert OPEN_URL.encode('utf-8') in res.data
# ...and the watch list now points at it instead of the watched URL
res = client.get(url_for("watchlist.index"))
assert f'href="{OPEN_URL}"'.encode('utf-8') in res.data
assert f'href="{test_url}"'.encode('utf-8') not in res.data
def test_link_to_open_used_in_history_header(client, live_server, measure_memory_usage, datastore_path):
set_original_response(datastore_path=datastore_path)
test_url = url_for('test_endpoint', _external=True)
_add_watch(client, test_url)
set_modified_response(datastore_path=datastore_path)
client.post(url_for("ui.form_watch_checknow"), follow_redirects=True)
wait_for_all_checks(client)
assert b"Updated watch." in _edit_watch(client, test_url, link_to_open=OPEN_URL).data
uuid = next(iter(client.application.config.get('DATASTORE').data['watching'].keys()))
# Top-of-page link on the history/diff page
res = client.get(url_for("ui.ui_diff.diff_history_page", uuid=uuid))
assert f'class="current-diff-url" href="{OPEN_URL}"'.encode('utf-8') in res.data
# ...and on the preview page
res = client.get(url_for("ui.ui_preview.preview_page", uuid=uuid))
assert f'class="current-diff-url" href="{OPEN_URL}"'.encode('utf-8') in res.data
def test_invalid_link_to_open_is_rejected(client, live_server, measure_memory_usage, datastore_path):
set_original_response(datastore_path=datastore_path)
test_url = url_for('test_endpoint', _external=True)
_add_watch(client, test_url)
res = _edit_watch(client, test_url, link_to_open="javascript:alert(1)")
assert b"Updated watch." not in res.data
assert b"Watch protocol is not permitted or invalid URL format" in res.data
# Blank is fine - it means "use the watched URL"
res = _edit_watch(client, test_url, link_to_open="")
assert b"Updated watch." in res.data
watch = list(client.application.config.get('DATASTORE').data['watching'].values())[0]
assert watch.open_link == test_url
def test_watch_open_url_notification_token(client, live_server, measure_memory_usage, datastore_path):
set_original_response(datastore_path=datastore_path)
notification_file = os.path.join(datastore_path, "notification.txt")
if os.path.isfile(notification_file):
os.unlink(notification_file)
test_url = url_for('test_endpoint', _external=True)
_add_watch(client, test_url)
notification_url = url_for('test_notification_endpoint', _external=True).replace('http://', 'post://')
res = _edit_watch(
client,
test_url,
link_to_open=OPEN_URL,
notification_urls=notification_url,
notification_title="Test",
notification_body="watched={{watch_url}}\nopen={{watch_open_url}}",
notification_format="text",
)
assert b"Updated watch." in res.data
wait_for_all_checks(client)
set_modified_response(datastore_path=datastore_path)
client.post(url_for("ui.form_watch_checknow"), follow_redirects=True)
wait_for_all_checks(client)
assert wait_for_notification_endpoint_output(datastore_path=datastore_path)
with open(notification_file, 'r') as f:
body = f.read()
assert f"watched={test_url}" in body
assert f"open={OPEN_URL}" in body
os.unlink(notification_file)
def test_watch_open_url_token_falls_back_to_watch_url(client, live_server, measure_memory_usage, datastore_path):
"""With no 'Link to Open' set, {{watch_open_url}} must still render the watched URL."""
set_original_response(datastore_path=datastore_path)
notification_file = os.path.join(datastore_path, "notification.txt")
if os.path.isfile(notification_file):
os.unlink(notification_file)
test_url = url_for('test_endpoint', _external=True)
_add_watch(client, test_url)
notification_url = url_for('test_notification_endpoint', _external=True).replace('http://', 'post://')
res = _edit_watch(
client,
test_url,
notification_urls=notification_url,
notification_title="Test",
notification_body="open={{watch_open_url}}",
notification_format="text",
)
assert b"Updated watch." in res.data
wait_for_all_checks(client)
set_modified_response(datastore_path=datastore_path)
client.post(url_for("ui.form_watch_checknow"), follow_redirects=True)
wait_for_all_checks(client)
assert wait_for_notification_endpoint_output(datastore_path=datastore_path)
with open(notification_file, 'r') as f:
body = f.read()
assert f"open={test_url}" in body
os.unlink(notification_file)