diff --git a/changedetectionio/tests/test_notification.py b/changedetectionio/tests/test_notification.py
index 20dc92a8..bcdacd57 100644
--- a/changedetectionio/tests/test_notification.py
+++ b/changedetectionio/tests/test_notification.py
@@ -636,11 +636,11 @@ def test_html_color_notifications(client, live_server, measure_memory_usage, dat
_test_color_notifications(client, '{{diff_full}}',datastore_path=datastore_path)
-def test_plaintext_watch_custom_html_in_notification_body_not_escaped(client, live_server, measure_memory_usage, datastore_path):
+def _test_custom_html_in_notification_body_not_escaped(client, datastore_path, content_type=None):
"""
- #4121 - When the watched URL returns text/plain content-type and the notification body
- contains custom HTML (e.g. ), that HTML must NOT be HTML-escaped
- in the output. Only the diff content (from the text/plain page) should be escaped.
+ #4121 - Custom HTML in the notification body (e.g. ) must NOT be
+ HTML-escaped regardless of the watched page's content-type. Only raw diff content from
+ text/plain pages needs escaping (to prevent raw '<' chars breaking HTML email rendering).
"""
set_original_response(datastore_path=datastore_path)
@@ -649,15 +649,14 @@ def test_plaintext_watch_custom_html_in_notification_body_not_escaped(client, li
test_notification_url = url_for('test_notification_endpoint', _external=True).replace('http://', 'post://')
- # Watch a URL that returns text/plain content-type
- test_url = url_for('test_endpoint', content_type="text/plain", _external=True)
+ kwargs = {'content_type': content_type} if content_type else {}
+ test_url = url_for('test_endpoint', _external=True, **kwargs)
res = client.post(
url_for("settings.settings_page"),
data={
"application-fetch_backend": "html_requests",
"application-minutes_between_check": 180,
- # Custom HTML in notification body — these tags must NOT be escaped in the output
"application-notification_body": 'Watch Link had changes\n\n{{diff}}',
"application-notification_format": "htmlcolor",
"application-notification_urls": test_notification_url,
@@ -686,12 +685,18 @@ def test_plaintext_watch_custom_html_in_notification_body_not_escaped(client, li
with open(os.path.join(datastore_path, "notification.txt"), 'r') as f:
x = f.read()
- # The custom from the notification template must NOT be HTML-escaped
- assert '<a href=' not in x, "Custom HTML tag in notification body was incorrectly HTML-escaped"
- assert ' tags from htmlcolor format not found in notification"
+ assert '<a href=' not in x, f"Custom HTML tag was incorrectly escaped (content_type={content_type})"
+ assert ' tags not found (content_type={content_type})"
client.get(url_for("ui.form_delete", uuid="all"), follow_redirects=True)
+
+def test_plaintext_watch_custom_html_in_notification_body_not_escaped(client, live_server, measure_memory_usage, datastore_path):
+ # text/plain: diff content may contain raw '<' chars — those must be escaped, but NOT the user's template HTML
+ _test_custom_html_in_notification_body_not_escaped(client, datastore_path, content_type="text/plain")
+ # text/html: HTML processor strips tags before diffing, no escaping needed, user's template HTML must be preserved
+ _test_custom_html_in_notification_body_not_escaped(client, datastore_path, content_type="text/html")
+ # no MIME type (None): same as HTML case, user's template HTML must be preserved
+ _test_custom_html_in_notification_body_not_escaped(client, datastore_path, content_type=None)
+