|
@@ -2,8 +2,15 @@ import unittest.mock
|
|
|
|
|
|
from django.apps import apps
|
|
|
from django.test import TestCase
|
|
|
+from django.utils.safestring import SafeString
|
|
|
|
|
|
-from wagtail.images.blocks import ImageChooserBlock
|
|
|
+from wagtail.admin import compare
|
|
|
+from wagtail.blocks.stream_block import StreamValue
|
|
|
+from wagtail.blocks.struct_block import StructBlockValidationError
|
|
|
+from wagtail.images.blocks import ImageBlock, ImageChooserBlock
|
|
|
+from wagtail.telepath import JSContext
|
|
|
+from wagtail.test.testapp.models import StreamPage
|
|
|
+from wagtail.test.utils.wagtail_tests import WagtailTestUtils
|
|
|
|
|
|
from .utils import (
|
|
|
Image,
|
|
@@ -73,3 +80,362 @@ class TestImageChooserBlock(TestCase):
|
|
|
|
|
|
# None should not yield any references
|
|
|
self.assertListEqual(list(block.extract_references(None)), [])
|
|
|
+
|
|
|
+
|
|
|
+class TestImageChooserBlockComparison(TestCase):
|
|
|
+ comparison_class = compare.StreamFieldComparison
|
|
|
+
|
|
|
+ def setUp(self):
|
|
|
+ self.image_1 = Image.objects.create(
|
|
|
+ title="Test image 1",
|
|
|
+ file=get_test_image_file(),
|
|
|
+ )
|
|
|
+
|
|
|
+ self.image_2 = Image.objects.create(
|
|
|
+ title="Test image 2",
|
|
|
+ file=get_test_image_file(),
|
|
|
+ )
|
|
|
+
|
|
|
+ self.field = StreamPage._meta.get_field("body")
|
|
|
+
|
|
|
+ def test_hasnt_changed(self):
|
|
|
+ field = StreamPage._meta.get_field("body")
|
|
|
+
|
|
|
+ comparison = self.comparison_class(
|
|
|
+ field,
|
|
|
+ StreamPage(
|
|
|
+ body=StreamValue(
|
|
|
+ field.stream_block,
|
|
|
+ [
|
|
|
+ ("image", self.image_1, "1"),
|
|
|
+ ],
|
|
|
+ )
|
|
|
+ ),
|
|
|
+ StreamPage(
|
|
|
+ body=StreamValue(
|
|
|
+ field.stream_block,
|
|
|
+ [
|
|
|
+ ("image", self.image_1, "1"),
|
|
|
+ ],
|
|
|
+ )
|
|
|
+ ),
|
|
|
+ )
|
|
|
+
|
|
|
+ self.assertTrue(comparison.is_field)
|
|
|
+ self.assertFalse(comparison.is_child_relation)
|
|
|
+ self.assertEqual(comparison.field_label(), "Body")
|
|
|
+ htmldiff = comparison.htmldiff()
|
|
|
+ self.assertIsInstance(htmldiff, SafeString)
|
|
|
+ self.assertIn('class="comparison__child-object"', htmldiff)
|
|
|
+ self.assertIn('class="preview-image"', htmldiff)
|
|
|
+ self.assertNotIn("deletion", htmldiff)
|
|
|
+ self.assertNotIn("addition", htmldiff)
|
|
|
+ self.assertFalse(comparison.has_changed())
|
|
|
+
|
|
|
+ def test_has_changed(self):
|
|
|
+ field = StreamPage._meta.get_field("body")
|
|
|
+
|
|
|
+ comparison = self.comparison_class(
|
|
|
+ field,
|
|
|
+ StreamPage(
|
|
|
+ body=StreamValue(
|
|
|
+ field.stream_block,
|
|
|
+ [
|
|
|
+ ("image", self.image_1, "1"),
|
|
|
+ ],
|
|
|
+ )
|
|
|
+ ),
|
|
|
+ StreamPage(
|
|
|
+ body=StreamValue(
|
|
|
+ field.stream_block,
|
|
|
+ [
|
|
|
+ ("image", self.image_2, "1"),
|
|
|
+ ],
|
|
|
+ )
|
|
|
+ ),
|
|
|
+ )
|
|
|
+
|
|
|
+ self.assertTrue(comparison.is_field)
|
|
|
+ self.assertFalse(comparison.is_child_relation)
|
|
|
+ self.assertEqual(comparison.field_label(), "Body")
|
|
|
+ htmldiff = comparison.htmldiff()
|
|
|
+ self.assertIsInstance(htmldiff, SafeString)
|
|
|
+ self.assertIn('class="comparison__child-object"', htmldiff)
|
|
|
+ self.assertIn('class="preview-image deletion"', htmldiff)
|
|
|
+ self.assertIn('class="preview-image addition"', htmldiff)
|
|
|
+ self.assertTrue(comparison.has_changed())
|
|
|
+
|
|
|
+
|
|
|
+class TestImageBlock(TestImageChooserBlock):
|
|
|
+ def test_render(self):
|
|
|
+ block = ImageBlock()
|
|
|
+ value = {
|
|
|
+ "image": self.image.id, # An id is expected
|
|
|
+ "alt_text": "Sample alt text",
|
|
|
+ "decorative": False,
|
|
|
+ }
|
|
|
+ html = block.render(block.to_python(value))
|
|
|
+ soup = WagtailTestUtils.get_soup(html)
|
|
|
+ img_tag = soup.find("img")
|
|
|
+
|
|
|
+ # check specific attributes
|
|
|
+ self.assertEqual(img_tag["alt"], value.get("alt_text"))
|
|
|
+ self.assertIn("/media/images/test", img_tag["src"])
|
|
|
+
|
|
|
+ def test_render_basic(self):
|
|
|
+ block = ImageBlock()
|
|
|
+ value = {
|
|
|
+ "image": self.image.id, # An id is expected
|
|
|
+ "alt_text": "Sample alt text",
|
|
|
+ "decorative": False,
|
|
|
+ }
|
|
|
+ html = block.render_basic(block.to_python(value))
|
|
|
+ soup = WagtailTestUtils.get_soup(html)
|
|
|
+ img_tag = soup.find("img")
|
|
|
+
|
|
|
+ # check specific attributes
|
|
|
+ self.assertEqual(img_tag["alt"], value.get("alt_text"))
|
|
|
+ self.assertIn("/media/images/test", img_tag["src"])
|
|
|
+
|
|
|
+ def test_render_as_decorative(self):
|
|
|
+ block = ImageBlock()
|
|
|
+ value = {
|
|
|
+ "image": self.image.id, # An id is expected
|
|
|
+ "alt_text": "Sample alt text",
|
|
|
+ "decorative": True,
|
|
|
+ }
|
|
|
+ html = block.render(block.to_python(value))
|
|
|
+ soup = WagtailTestUtils.get_soup(html)
|
|
|
+ img_tag = soup.find("img")
|
|
|
+
|
|
|
+ # check specific attributes
|
|
|
+ self.assertEqual(img_tag["alt"], "")
|
|
|
+ self.assertIn("/media/images/test", img_tag["src"])
|
|
|
+
|
|
|
+ def test_no_alt_text(self):
|
|
|
+ block = ImageBlock()
|
|
|
+ value = {
|
|
|
+ "image": self.image.id, # An id is expected
|
|
|
+ "alt_text": None, # No alt text passed
|
|
|
+ "decorative": False,
|
|
|
+ }
|
|
|
+
|
|
|
+ # Invalid state when no alt text is given, and image not marked as decorative
|
|
|
+ # Should raise a StructBlock validation error
|
|
|
+ with self.assertRaises(StructBlockValidationError) as context:
|
|
|
+ block.clean(block.to_python(value))
|
|
|
+
|
|
|
+ # Check the error message
|
|
|
+ self.assertIn(
|
|
|
+ "Please add some alt text for your image or mark it as decorative",
|
|
|
+ str(context.exception.block_errors["alt_text"]),
|
|
|
+ )
|
|
|
+
|
|
|
+ def test_to_python_with_int(self):
|
|
|
+ block = ImageBlock()
|
|
|
+ value = block.to_python(self.image.id)
|
|
|
+
|
|
|
+ self.assertEqual(value.id, self.image.id)
|
|
|
+ self.assertEqual(value.contextual_alt_text, None)
|
|
|
+ self.assertFalse(value.decorative)
|
|
|
+
|
|
|
+ def test_to_python_with_dict(self):
|
|
|
+ block = ImageBlock()
|
|
|
+ value = {"image": self.image.id, "alt_text": "Sample text", "decorative": False}
|
|
|
+ result = block.to_python(value)
|
|
|
+
|
|
|
+ self.assertEqual(result.id, self.image.id)
|
|
|
+ self.assertEqual(result.contextual_alt_text, "Sample text")
|
|
|
+ self.assertFalse(result.decorative)
|
|
|
+
|
|
|
+ def test_get_searchable_content(self):
|
|
|
+ block = ImageBlock()
|
|
|
+ value = {
|
|
|
+ "image": self.image.id, # An id is expected
|
|
|
+ "alt_text": "Sample alt text",
|
|
|
+ "decorative": False,
|
|
|
+ }
|
|
|
+ result = block.get_searchable_content(block.to_python(value))
|
|
|
+
|
|
|
+ # check specific attributes
|
|
|
+ self.assertEqual(result, ["Sample alt text"])
|
|
|
+
|
|
|
+ def test_required_true(self):
|
|
|
+ block = ImageBlock()
|
|
|
+
|
|
|
+ # the inner ImageChooserBlock should appear as required
|
|
|
+ image_block_def = JSContext().pack(block)
|
|
|
+ image_chooser_block_def = image_block_def["_args"][1][0]
|
|
|
+ self.assertTrue(image_chooser_block_def["_args"][2]["required"])
|
|
|
+
|
|
|
+ value = block.to_python(
|
|
|
+ {
|
|
|
+ "image": None,
|
|
|
+ "alt_text": "",
|
|
|
+ "decorative": False,
|
|
|
+ }
|
|
|
+ )
|
|
|
+ with self.assertRaises(StructBlockValidationError) as context:
|
|
|
+ block.clean(value)
|
|
|
+
|
|
|
+ self.assertIn(
|
|
|
+ "This field is required",
|
|
|
+ str(context.exception.block_errors["image"]),
|
|
|
+ )
|
|
|
+
|
|
|
+ def test_required_false(self):
|
|
|
+ block = ImageBlock(required=False)
|
|
|
+
|
|
|
+ # the inner ImageChooserBlock should appear as non-required
|
|
|
+ image_block_def = JSContext().pack(block)
|
|
|
+ image_chooser_block_def = image_block_def["_args"][1][0]
|
|
|
+ self.assertFalse(image_chooser_block_def["_args"][2]["required"])
|
|
|
+
|
|
|
+ value = block.to_python(
|
|
|
+ {
|
|
|
+ "image": None,
|
|
|
+ "alt_text": "",
|
|
|
+ "decorative": False,
|
|
|
+ }
|
|
|
+ )
|
|
|
+ self.assertIsNone(block.clean(value))
|
|
|
+
|
|
|
+
|
|
|
+class TestImageBlockComparison(TestCase):
|
|
|
+ comparison_class = compare.StreamFieldComparison
|
|
|
+
|
|
|
+ def setUp(self):
|
|
|
+ self.image_1 = Image.objects.create(
|
|
|
+ title="Test image 1",
|
|
|
+ file=get_test_image_file(),
|
|
|
+ )
|
|
|
+
|
|
|
+ self.image_2 = Image.objects.create(
|
|
|
+ title="Test image 2",
|
|
|
+ file=get_test_image_file(),
|
|
|
+ )
|
|
|
+
|
|
|
+ self.field = StreamPage._meta.get_field("body")
|
|
|
+
|
|
|
+ def test_hasnt_changed(self):
|
|
|
+ field = StreamPage._meta.get_field("body")
|
|
|
+
|
|
|
+ page_1 = StreamPage()
|
|
|
+ page_1.body = [
|
|
|
+ {
|
|
|
+ "type": "image_with_alt",
|
|
|
+ "value": {
|
|
|
+ "image": self.image_1.id,
|
|
|
+ "decorative": False,
|
|
|
+ "alt_text": "Some alt text",
|
|
|
+ },
|
|
|
+ "id": "1",
|
|
|
+ },
|
|
|
+ ]
|
|
|
+ page_2 = StreamPage()
|
|
|
+ page_2.body = [
|
|
|
+ {
|
|
|
+ "type": "image_with_alt",
|
|
|
+ "value": {
|
|
|
+ "image": self.image_1.id,
|
|
|
+ "decorative": False,
|
|
|
+ "alt_text": "Some alt text",
|
|
|
+ },
|
|
|
+ "id": "1",
|
|
|
+ },
|
|
|
+ ]
|
|
|
+
|
|
|
+ comparison = self.comparison_class(field, page_1, page_2)
|
|
|
+
|
|
|
+ self.assertTrue(comparison.is_field)
|
|
|
+ self.assertFalse(comparison.is_child_relation)
|
|
|
+ self.assertEqual(comparison.field_label(), "Body")
|
|
|
+ htmldiff = comparison.htmldiff()
|
|
|
+ self.assertIsInstance(htmldiff, SafeString)
|
|
|
+ self.assertIn('class="comparison__child-object"', htmldiff)
|
|
|
+ self.assertIn('class="preview-image"', htmldiff)
|
|
|
+ self.assertNotIn("deletion", htmldiff)
|
|
|
+ self.assertNotIn("addition", htmldiff)
|
|
|
+ self.assertFalse(comparison.has_changed())
|
|
|
+
|
|
|
+ def test_has_changed(self):
|
|
|
+ field = StreamPage._meta.get_field("body")
|
|
|
+
|
|
|
+ page_1 = StreamPage()
|
|
|
+ page_1.body = [
|
|
|
+ {
|
|
|
+ "type": "image_with_alt",
|
|
|
+ "value": {
|
|
|
+ "image": self.image_1.id,
|
|
|
+ "decorative": False,
|
|
|
+ "alt_text": "Some alt text",
|
|
|
+ },
|
|
|
+ "id": "1",
|
|
|
+ },
|
|
|
+ ]
|
|
|
+ page_2 = StreamPage()
|
|
|
+ page_2.body = [
|
|
|
+ {
|
|
|
+ "type": "image_with_alt",
|
|
|
+ "value": {
|
|
|
+ "image": self.image_2.id,
|
|
|
+ "decorative": False,
|
|
|
+ "alt_text": "Some alt text",
|
|
|
+ },
|
|
|
+ "id": "1",
|
|
|
+ },
|
|
|
+ ]
|
|
|
+
|
|
|
+ comparison = self.comparison_class(field, page_1, page_2)
|
|
|
+
|
|
|
+ self.assertTrue(comparison.is_field)
|
|
|
+ self.assertFalse(comparison.is_child_relation)
|
|
|
+ self.assertEqual(comparison.field_label(), "Body")
|
|
|
+ htmldiff = comparison.htmldiff()
|
|
|
+ self.assertIsInstance(htmldiff, SafeString)
|
|
|
+ self.assertIn('class="comparison__child-object"', htmldiff)
|
|
|
+ self.assertIn('class="preview-image deletion"', htmldiff)
|
|
|
+ self.assertIn('class="preview-image addition"', htmldiff)
|
|
|
+ self.assertTrue(comparison.has_changed())
|
|
|
+
|
|
|
+ def test_alt_text_has_changed(self):
|
|
|
+ field = StreamPage._meta.get_field("body")
|
|
|
+
|
|
|
+ page_1 = StreamPage()
|
|
|
+ page_1.body = [
|
|
|
+ {
|
|
|
+ "type": "image_with_alt",
|
|
|
+ "value": {
|
|
|
+ "image": self.image_1.id,
|
|
|
+ "decorative": False,
|
|
|
+ "alt_text": "a cat playing with some string",
|
|
|
+ },
|
|
|
+ "id": "1",
|
|
|
+ },
|
|
|
+ ]
|
|
|
+ page_2 = StreamPage()
|
|
|
+ page_2.body = [
|
|
|
+ {
|
|
|
+ "type": "image_with_alt",
|
|
|
+ "value": {
|
|
|
+ "image": self.image_1.id,
|
|
|
+ "decorative": False,
|
|
|
+ "alt_text": "a kitten playing with some string",
|
|
|
+ },
|
|
|
+ "id": "1",
|
|
|
+ },
|
|
|
+ ]
|
|
|
+
|
|
|
+ comparison = self.comparison_class(field, page_1, page_2)
|
|
|
+
|
|
|
+ self.assertTrue(comparison.is_field)
|
|
|
+ self.assertFalse(comparison.is_child_relation)
|
|
|
+ self.assertEqual(comparison.field_label(), "Body")
|
|
|
+ htmldiff = comparison.htmldiff()
|
|
|
+ self.assertIsInstance(htmldiff, SafeString)
|
|
|
+ self.assertIn('class="comparison__child-object"', htmldiff)
|
|
|
+ self.assertIn(
|
|
|
+ '<dd>a <span class="deletion">cat</span><span class="addition">kitten</span> playing with some string</dd>',
|
|
|
+ htmldiff,
|
|
|
+ )
|
|
|
+ self.assertTrue(comparison.has_changed())
|