import copy
from unittest import skipIf
from unittest.mock import patch, MagicMock
from django.test import TestCase
try:
from cms.api import create_page, add_plugin
from cms.test_utils.testcases import CMSTestCase
from lxml.etree import Element
from djangocms_text import html, settings
from djangocms_text.html import NH3Parser, dynamic_href, dynamic_src, render_dynamic_attributes
SKIP_CMS_TEST = False
except ModuleNotFoundError:
from django.test.utils import TestCase as CMSTestCase
SKIP_CMS_TEST = True
from tests.fixtures import DJANGO_CMS4, TestFixture
@skipIf(SKIP_CMS_TEST, "Skipping tests because djangocms is not installed")
class SanitizerTestCase(TestCase):
def test_sanitizer(self):
body = 'some text'
body = html.clean_html(body)
self.assertTrue('data-one="1"' in body)
self.assertTrue('data-two="2"' in body)
def test_sanitizer_with_custom_token_parser(self):
cleaner = NH3Parser(additional_attributes={"span": {"donut"}})
body = 'some text'
body = html.clean_html(body, cleaner=cleaner)
self.assertEqual('some text', body)
def test_sanitizer_without_token_parsers(self):
body = 'some text'
body = html.clean_html(body, cleaner=NH3Parser(generic_attribute_prefixes=set()))
self.assertEqual("some text", body)
def test_attribute_merging(self):
# Backup the original global attributes.
original_global_attributes = copy.deepcopy(getattr(settings, "TEXT_ADDITIONAL_ATTRIBUTES", {}))
# Configure global attributes.
settings.TEXT_ADDITIONAL_ATTRIBUTES = {"span": {"global-attr"}}
# Instantiate NH3Parser with instance-specific additional attributes.
parser = NH3Parser(additional_attributes={"span": {"local-attr"}})
input_html = 'text'
cleaned = html.clean_html(input_html, cleaner=parser)
# Assert both attributes are present in the cleaned output.
self.assertIn('global-attr="g"', cleaned)
self.assertIn('local-attr="l"', cleaned)
# Restore the original global settings.
settings.TEXT_ADDITIONAL_ATTRIBUTES = original_global_attributes
@skipIf(SKIP_CMS_TEST, "Skipping tests because djangocms is not installed")
class HtmlSanitizerAdditionalProtocolsTests(CMSTestCase):
def test_default_tag_removal(self):
settings.TEXT_ADDITIONAL_ATTRIBUTES = {}
text = html.clean_html(
'',
cleaner=NH3Parser(),
)
self.assertNotIn("iframe", NH3Parser().ALLOWED_TAGS)
self.assertEqual(
"",
text,
)
def test_custom_tag_enabled(self):
text = html.clean_html(
'',
cleaner=NH3Parser(
additional_attributes={"iframe": {"src"}},
),
)
self.assertEqual(
'',
text,
)
def test_default_attribute_escaping(self):
text = html.clean_html(
'foo',
cleaner=NH3Parser(),
)
self.assertEqual(
"foo",
text,
)
def test_custom_attribute_enabled(self):
text = html.clean_html(
'foo',
cleaner=NH3Parser(
additional_attributes={
"span": {"test-attr"},
},
),
)
self.assertEqual(
'foo',
text,
)
def test_default_protocol_removal(self):
settings.TEXT_ADDITIONAL_PROTOCOLS = []
text = html.clean_html(
'',
cleaner=NH3Parser(),
)
self.assertEqual("", text)
def test_custom_protocol_enabled(self):
settings.TEXT_ADDITIONAL_PROTOCOLS = ["rtmp"]
text = html.clean_html('', cleaner=NH3Parser())
self.assertEqual('', text)
def test_clean_html_with_sanitize_enabled(self):
old_text_html_sanitize = settings.TEXT_HTML_SANITIZE
settings.TEXT_HTML_SANITIZE = True
original = 'foo'
cleaned = html.clean_html(
original,
)
try:
self.assertHTMLEqual("foo", cleaned)
finally:
settings.TEXT_HTML_SANITIZE = old_text_html_sanitize
def test_clean_html_with_sanitize_disabled(self):
old_text_html_sanitize = settings.TEXT_HTML_SANITIZE
settings.TEXT_HTML_SANITIZE = False
original = 'foo'
cleaned = html.clean_html(
original,
)
try:
self.assertHTMLEqual(original, cleaned)
finally:
settings.TEXT_HTML_SANITIZE = old_text_html_sanitize
def test_clean_html_preserves_aria_attributes(self):
original = 'foo'
cleaned = html.clean_html(
original,
)
self.assertHTMLEqual(original, cleaned)
def test_clean_html_preserves_data_attributes(self):
original = 'foo'
cleaned = html.clean_html(
original,
)
self.assertHTMLEqual(original, cleaned)
def test_clean_html_preserves_role_attribute(self):
original = 'foo'
cleaned = html.clean_html(
original,
)
self.assertHTMLEqual(original, cleaned)
@skipIf(SKIP_CMS_TEST, "Skipping tests because djangocms is not installed")
class HTMLDynamicAttriutesTest(TestFixture, CMSTestCase):
def test_dynamic_link(self):
page = self.create_page("page", "page.html", language="en")
self.publish(page, "en")
self.assertEqual(
page.get_absolute_url(),
"/en/page/",
)
dynamic_html = f'Link'
result = render_dynamic_attributes(dynamic_html)
self.assertEqual(
result,
f'Link',
)
def test_invalid_dynamic_link(self):
page = self.create_page("page", "page.html", language="en")
self.publish(page, "en")
self.assertEqual(
page.get_absolute_url(),
"/en/page/",
)
dynamic_html = 'Link'
result = render_dynamic_attributes(dynamic_html)
self.assertEqual(
result,
'Link',
)
@skipIf(SKIP_CMS_TEST, "Skipping tests because djangocms is not installed")
class DynamicAttributesTestCase(TestCase):
@patch("djangocms_text.html.apps.get_model")
def test_dynamic_href_sets_correct_attribute(self, mock_get_model):
mock_obj = MagicMock()
mock_obj.get_absolute_url.return_value = "/test-url"
mock_get_model.return_value.objects.filter.return_value = [mock_obj]
elem = Element("a", {"data-cms-href": "app.Model:1"})
dynamic_href(elem, mock_obj, "href")
self.assertEqual(elem.attrib["href"], "/test-url")
@patch("djangocms_text.html.apps.get_model")
def test_dynamic_src_sets_correct_attribute(self, mock_get_model):
mock_obj = MagicMock()
mock_obj.get_absolute_url.return_value = "/test-url"
mock_get_model.return_value.objects.filter.return_value = [mock_obj]
elem = Element("img", {"data-cms-src": "app.Model:1"})
dynamic_src(elem, mock_obj, "src")
self.assertEqual(elem.attrib["src"], "/test-url")
def test_render_dynamic_attributes_changes_html(self):
page = create_page("page", "page.html", language="en")
html = f'Link'
updated_html = render_dynamic_attributes(html)
self.assertIn('Link', updated_html)
def test_render_dynamic_attributes_fails(self):
html = 'Link'
updated_html = render_dynamic_attributes(html)
self.assertIn('Link', updated_html)
def test_render_dynamic_attributes_fails_edit_mode(self):
html = 'Link'
updated_html = render_dynamic_attributes(html, admin_objects=True)
self.assertIn('Link', updated_html)
def test_render_dynamic_attributes_handles_no_dynamic_attributes(self):
html = "No dynamic attributes
"
updated_html = render_dynamic_attributes(html)
self.assertEqual(html, updated_html)
def save_image(filename, image, parent_plugin, width, height):
pass
@skipIf(SKIP_CMS_TEST, "Skipping tests because djangocms is not installed")
class DjangoCMSPictureIntegrationTestCase(CMSTestCase):
def setUp(self):
super().setUp()
self.home = self.create_homepage("home", "page.html", "en")
if DJANGO_CMS4:
self.placeholder = (
self.home.pagecontent_set(manager="admin_manager").first().get_placeholders().get(slot="content")
)
else:
self.placeholder = self.home.get_placeholders().get(slot="content")
def test_extract_images(self):
with patch("tests.test_html.save_image") as mock_save_image:
add_plugin(
self.placeholder,
"TextPlugin",
"en",
body='
',
)
mock_save_image.assert_called_once()