Initial commit
0
env/lib/python3.10/site-packages/wagtail/images/tests/__init__.py
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/__pycache__/__init__.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/__pycache__/test_admin_views.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/__pycache__/test_api_fields.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/__pycache__/test_blocks.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/__pycache__/test_jinja2.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/__pycache__/test_models.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/__pycache__/test_rich_text.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/__pycache__/test_shortcuts.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/__pycache__/test_site_summary.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/__pycache__/test_templatetags.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/__pycache__/test_transform.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/__pycache__/tests.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/__pycache__/urls.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/__pycache__/utils.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/image_files/landscape_1.jpg
vendored
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/image_files/landscape_2.jpg
vendored
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/image_files/landscape_3.jpg
vendored
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/image_files/landscape_4.jpg
vendored
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/image_files/landscape_5.jpg
vendored
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/image_files/landscape_6.jpg
vendored
Normal file
|
After Width: | Height: | Size: 127 KiB |
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/image_files/landscape_7.jpg
vendored
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
env/lib/python3.10/site-packages/wagtail/images/tests/image_files/landscape_8.jpg
vendored
Normal file
|
After Width: | Height: | Size: 131 KiB |
3694
env/lib/python3.10/site-packages/wagtail/images/tests/test_admin_views.py
vendored
Normal file
25
env/lib/python3.10/site-packages/wagtail/images/tests/test_api_fields.py
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from wagtail.images.api.fields import ImageRenditionField
|
||||
|
||||
from .utils import Image, get_test_image_file
|
||||
|
||||
|
||||
class TestImageRenditionField(TestCase):
|
||||
def setUp(self):
|
||||
self.image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
def test_api_representation(self):
|
||||
rendition = self.image.get_rendition("width-400")
|
||||
representation = ImageRenditionField("width-400").to_representation(self.image)
|
||||
self.assertEqual(
|
||||
set(representation.keys()), {"url", "full_url", "width", "height", "alt"}
|
||||
)
|
||||
self.assertEqual(representation["url"], rendition.url)
|
||||
self.assertEqual(representation["full_url"], rendition.full_url)
|
||||
self.assertEqual(representation["width"], rendition.width)
|
||||
self.assertEqual(representation["height"], rendition.height)
|
||||
self.assertEqual(representation["alt"], rendition.alt)
|
||||
75
env/lib/python3.10/site-packages/wagtail/images/tests/test_blocks.py
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
import unittest.mock
|
||||
|
||||
from django.apps import apps
|
||||
from django.test import TestCase
|
||||
|
||||
from wagtail.images.blocks import ImageChooserBlock
|
||||
|
||||
from .utils import (
|
||||
Image,
|
||||
get_test_bad_image,
|
||||
get_test_image_file,
|
||||
get_test_image_filename,
|
||||
)
|
||||
|
||||
|
||||
class TestImageChooserBlock(TestCase):
|
||||
def setUp(self):
|
||||
self.image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
self.bad_image = get_test_bad_image()
|
||||
self.bad_image.save()
|
||||
|
||||
def test_render(self):
|
||||
block = ImageChooserBlock()
|
||||
html = block.render(self.image)
|
||||
expected_html = (
|
||||
'<img alt="Test image" src="{}" width="640" height="480">'.format(
|
||||
get_test_image_filename(self.image, "original")
|
||||
)
|
||||
)
|
||||
|
||||
self.assertHTMLEqual(html, expected_html)
|
||||
|
||||
def test_render_with_custom_default_attrs(self):
|
||||
block = ImageChooserBlock()
|
||||
with unittest.mock.patch.object(
|
||||
apps.get_app_config("wagtailimages"),
|
||||
"default_attrs",
|
||||
new={"decoding": "async", "loading": "lazy"},
|
||||
):
|
||||
html = block.render(self.bad_image)
|
||||
self.assertHTMLEqual(
|
||||
html,
|
||||
'<img alt="missing image" src="/media/not-found" width="0" height="0" decoding="async" loading="lazy">',
|
||||
)
|
||||
|
||||
def test_render_missing(self):
|
||||
block = ImageChooserBlock()
|
||||
html = block.render(self.bad_image)
|
||||
expected_html = (
|
||||
'<img alt="missing image" src="/media/not-found" width="0" height="0">'
|
||||
)
|
||||
|
||||
self.assertHTMLEqual(html, expected_html)
|
||||
|
||||
def test_deconstruct(self):
|
||||
block = ImageChooserBlock(required=False)
|
||||
path, args, kwargs = block.deconstruct()
|
||||
self.assertEqual(path, "wagtail.images.blocks.ImageChooserBlock")
|
||||
self.assertEqual(args, ())
|
||||
self.assertEqual(kwargs, {"required": False})
|
||||
|
||||
def test_extract_references(self):
|
||||
block = ImageChooserBlock()
|
||||
|
||||
self.assertListEqual(
|
||||
list(block.extract_references(self.image)),
|
||||
[(Image, str(self.image.id), "", "")],
|
||||
)
|
||||
|
||||
# None should not yield any references
|
||||
self.assertListEqual(list(block.extract_references(None)), [])
|
||||
0
env/lib/python3.10/site-packages/wagtail/images/tests/test_bulk_actions/__init__.py
vendored
Normal file
84
env/lib/python3.10/site-packages/wagtail/images/tests/test_bulk_actions/test_bulk_add_tags.py
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from wagtail.images import get_image_model
|
||||
from wagtail.images.tests.utils import get_test_image_file
|
||||
from wagtail.test.utils import WagtailTestUtils
|
||||
|
||||
Image = get_image_model()
|
||||
test_file = get_test_image_file()
|
||||
|
||||
|
||||
def get_tag_list(image):
|
||||
return [tag.name for tag in image.tags.all()]
|
||||
|
||||
|
||||
class TestBulkAddTags(WagtailTestUtils, TestCase):
|
||||
def setUp(self):
|
||||
self.user = self.login()
|
||||
self.new_tags = ["first", "second"]
|
||||
self.images = [
|
||||
Image.objects.create(title=f"Test image - {i}", file=test_file)
|
||||
for i in range(1, 6)
|
||||
]
|
||||
self.url = (
|
||||
reverse(
|
||||
"wagtail_bulk_action",
|
||||
args=(
|
||||
"wagtailimages",
|
||||
"image",
|
||||
"add_tags",
|
||||
),
|
||||
)
|
||||
+ "?"
|
||||
)
|
||||
for image in self.images:
|
||||
self.url += f"id={image.id}&"
|
||||
self.post_data = {"tags": ",".join(self.new_tags)}
|
||||
|
||||
def test_add_tags_with_limited_permissions(self):
|
||||
self.user.is_superuser = False
|
||||
self.user.user_permissions.add(
|
||||
Permission.objects.get(
|
||||
content_type__app_label="wagtailadmin", codename="access_admin"
|
||||
)
|
||||
)
|
||||
self.user.save()
|
||||
|
||||
response = self.client.get(self.url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
html = response.content.decode()
|
||||
self.assertInHTML(
|
||||
"<p>You don't have permission to add tags to these images</p>", html
|
||||
)
|
||||
|
||||
for image in self.images:
|
||||
self.assertInHTML(f"<li>{image.title}</li>", html)
|
||||
|
||||
self.client.post(self.url, self.post_data)
|
||||
|
||||
# New tags should not be added to the images
|
||||
for image in self.images:
|
||||
self.assertCountEqual(get_tag_list(Image.objects.get(id=image.id)), [])
|
||||
|
||||
def test_simple(self):
|
||||
response = self.client.get(self.url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(
|
||||
response, "wagtailimages/bulk_actions/confirm_bulk_add_tags.html"
|
||||
)
|
||||
|
||||
def test_add_tags(self):
|
||||
# Make post request
|
||||
response = self.client.post(self.url, self.post_data)
|
||||
|
||||
# User should be redirected back to the index
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
# New tags should not be added to the images
|
||||
for image in self.images:
|
||||
self.assertCountEqual(
|
||||
get_tag_list(Image.objects.get(id=image.id)), self.new_tags
|
||||
)
|
||||
@@ -0,0 +1,84 @@
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from wagtail.images import get_image_model
|
||||
from wagtail.images.tests.utils import get_test_image_file
|
||||
from wagtail.models import Collection
|
||||
from wagtail.test.utils import WagtailTestUtils
|
||||
|
||||
Image = get_image_model()
|
||||
test_file = get_test_image_file()
|
||||
|
||||
|
||||
class TestBulkAddImagesToCollection(WagtailTestUtils, TestCase):
|
||||
def setUp(self):
|
||||
self.user = self.login()
|
||||
self.root_collection = Collection.get_first_root_node()
|
||||
self.dest_collection = self.root_collection.add_child(name="Destination")
|
||||
self.images = [
|
||||
Image.objects.create(title=f"Test image - {i}", file=test_file)
|
||||
for i in range(1, 6)
|
||||
]
|
||||
self.url = (
|
||||
reverse(
|
||||
"wagtail_bulk_action",
|
||||
args=(
|
||||
"wagtailimages",
|
||||
"image",
|
||||
"add_to_collection",
|
||||
),
|
||||
)
|
||||
+ "?"
|
||||
)
|
||||
for image in self.images:
|
||||
self.url += f"id={image.id}&"
|
||||
self.post_data = {"collection": str(self.dest_collection.id)}
|
||||
|
||||
def test_add_to_collection_with_limited_permissions(self):
|
||||
self.user.is_superuser = False
|
||||
self.user.user_permissions.add(
|
||||
Permission.objects.get(
|
||||
content_type__app_label="wagtailadmin", codename="access_admin"
|
||||
)
|
||||
)
|
||||
self.user.save()
|
||||
|
||||
response = self.client.get(self.url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
html = response.content.decode()
|
||||
self.assertInHTML(
|
||||
"<p>You don't have permission to add these images to a collection</p>", html
|
||||
)
|
||||
|
||||
for image in self.images:
|
||||
self.assertInHTML(f"<li>{image.title}</li>", html)
|
||||
|
||||
self.client.post(self.url, self.post_data)
|
||||
|
||||
# Images should not be moved to new collection
|
||||
for image in self.images:
|
||||
self.assertEqual(
|
||||
Image.objects.get(id=image.id).collection_id, self.root_collection.id
|
||||
)
|
||||
|
||||
def test_simple(self):
|
||||
response = self.client.get(self.url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(
|
||||
response, "wagtailimages/bulk_actions/confirm_bulk_add_to_collection.html"
|
||||
)
|
||||
|
||||
def test_add_to_collection(self):
|
||||
# Make post request
|
||||
response = self.client.post(self.url, self.post_data)
|
||||
|
||||
# User should be redirected back to the index
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
# Images should be moved to new collection
|
||||
for image in self.images:
|
||||
self.assertEqual(
|
||||
Image.objects.get(id=image.id).collection_id, self.dest_collection.id
|
||||
)
|
||||
91
env/lib/python3.10/site-packages/wagtail/images/tests/test_bulk_actions/test_bulk_delete.py
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from wagtail.images import get_image_model
|
||||
from wagtail.images.tests.utils import get_test_image_file
|
||||
from wagtail.test.utils import WagtailTestUtils
|
||||
|
||||
Image = get_image_model()
|
||||
test_file = get_test_image_file()
|
||||
|
||||
|
||||
class TestImageBulkDeleteView(WagtailTestUtils, TestCase):
|
||||
def setUp(self):
|
||||
self.user = self.login()
|
||||
|
||||
# Create images to delete
|
||||
self.images = [
|
||||
Image.objects.create(title=f"Test image - {i}", file=test_file)
|
||||
for i in range(1, 6)
|
||||
]
|
||||
self.url = (
|
||||
reverse(
|
||||
"wagtail_bulk_action",
|
||||
args=(
|
||||
"wagtailimages",
|
||||
"image",
|
||||
"delete",
|
||||
),
|
||||
)
|
||||
+ "?"
|
||||
)
|
||||
for image in self.images:
|
||||
self.url += f"id={image.id}&"
|
||||
|
||||
def test_delete_with_limited_permissions(self):
|
||||
self.user.is_superuser = False
|
||||
self.user.user_permissions.add(
|
||||
Permission.objects.get(
|
||||
content_type__app_label="wagtailadmin", codename="access_admin"
|
||||
)
|
||||
)
|
||||
self.user.save()
|
||||
|
||||
response = self.client.get(self.url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
html = response.content.decode()
|
||||
self.assertInHTML(
|
||||
"<p>You don't have permission to delete these images</p>", html
|
||||
)
|
||||
|
||||
for image in self.images:
|
||||
self.assertInHTML(f"<li>{image.title}</li>", html)
|
||||
|
||||
response = self.client.post(self.url)
|
||||
# User should be redirected back to the index
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
# Images should not be deleted
|
||||
for image in self.images:
|
||||
self.assertTrue(Image.objects.filter(id=image.id).exists())
|
||||
|
||||
def test_simple(self):
|
||||
response = self.client.get(self.url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(
|
||||
response, "wagtailimages/bulk_actions/confirm_bulk_delete.html"
|
||||
)
|
||||
|
||||
def test_delete(self):
|
||||
# Make post request
|
||||
response = self.client.post(self.url)
|
||||
|
||||
# User should be redirected back to the index
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
# Images should be deleted
|
||||
for image in self.images:
|
||||
self.assertFalse(Image.objects.filter(id=image.id).exists())
|
||||
|
||||
def test_usage_link(self):
|
||||
response = self.client.get(self.url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(
|
||||
response, "wagtailimages/bulk_actions/confirm_bulk_delete.html"
|
||||
)
|
||||
for image in self.images:
|
||||
self.assertContains(response, image.usage_url)
|
||||
# usage count should be printed for each image
|
||||
self.assertContains(response, "Used 0 times", count=5)
|
||||
83
env/lib/python3.10/site-packages/wagtail/images/tests/test_form_overrides.py
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
from django import forms
|
||||
from django.test import TestCase, override_settings
|
||||
from taggit import models as taggit_models
|
||||
|
||||
from wagtail.admin import widgets
|
||||
from wagtail.admin.widgets import AdminDateTimeInput
|
||||
from wagtail.images import models
|
||||
from wagtail.images.forms import BaseImageForm, get_image_base_form, get_image_form
|
||||
from wagtail.test.testapp.media_forms import AlternateImageForm, OverriddenWidget
|
||||
from wagtail.test.testapp.models import CustomRestaurantImage, RestaurantTag
|
||||
|
||||
|
||||
class TestImageFormOverride(TestCase):
|
||||
def test_get_image_base_form(self):
|
||||
self.assertIs(get_image_base_form(), BaseImageForm)
|
||||
|
||||
def test_get_image_form(self):
|
||||
bases = get_image_form(models.Image).__bases__
|
||||
self.assertIn(BaseImageForm, bases)
|
||||
self.assertNotIn(AlternateImageForm, bases)
|
||||
|
||||
def test_get_image_form_widgets(self):
|
||||
form_cls = get_image_form(models.Image)
|
||||
form = form_cls()
|
||||
self.assertIsInstance(form.fields["tags"].widget, widgets.AdminTagWidget)
|
||||
self.assertEqual(form.fields["tags"].widget.tag_model, taggit_models.Tag)
|
||||
self.assertIsInstance(form.fields["file"].widget, forms.FileInput)
|
||||
self.assertIsInstance(form.fields["focal_point_x"].widget, forms.HiddenInput)
|
||||
|
||||
def test_tags_widget_with_custom_tag_model(self):
|
||||
form_cls = get_image_form(CustomRestaurantImage)
|
||||
form = form_cls()
|
||||
self.assertIsInstance(form.fields["tags"].widget, widgets.AdminTagWidget)
|
||||
self.assertEqual(form.fields["tags"].widget.tag_model, RestaurantTag)
|
||||
|
||||
def test_tags_longer_than_max_characters(self):
|
||||
long_value = "longtag" * 20
|
||||
|
||||
form_data = {
|
||||
"title": "Image",
|
||||
"file": OverriddenWidget,
|
||||
"tags": [long_value],
|
||||
}
|
||||
|
||||
form_cls = get_image_form(models.Image)
|
||||
form = form_cls(form_data)
|
||||
|
||||
self.assertFalse(form.is_valid())
|
||||
self.assertIn("tags", form.errors)
|
||||
self.assertEqual(
|
||||
form.errors["tags"][0],
|
||||
"Tag(s) ['{val}'] are over {max_tag_length} characters".format(
|
||||
val=long_value,
|
||||
max_tag_length=taggit_models.TagBase._meta.get_field("name").max_length,
|
||||
),
|
||||
)
|
||||
|
||||
@override_settings(
|
||||
WAGTAILIMAGES_IMAGE_FORM_BASE="wagtail.test.testapp.media_forms.AlternateImageForm"
|
||||
)
|
||||
def test_overridden_base_form(self):
|
||||
self.assertIs(get_image_base_form(), AlternateImageForm)
|
||||
|
||||
@override_settings(
|
||||
WAGTAILIMAGES_IMAGE_FORM_BASE="wagtail.test.testapp.media_forms.AlternateImageForm"
|
||||
)
|
||||
def test_get_overridden_image_form(self):
|
||||
bases = get_image_form(models.Image).__bases__
|
||||
self.assertNotIn(BaseImageForm, bases)
|
||||
self.assertIn(AlternateImageForm, bases)
|
||||
|
||||
@override_settings(
|
||||
WAGTAILIMAGES_IMAGE_FORM_BASE="wagtail.test.testapp.media_forms.AlternateImageForm"
|
||||
)
|
||||
def test_get_overridden_image_form_widgets(self):
|
||||
form_cls = get_image_form(models.Image)
|
||||
form = form_cls()
|
||||
self.assertIsInstance(form.fields["tags"].widget, OverriddenWidget)
|
||||
self.assertIsInstance(form.fields["file"].widget, OverriddenWidget)
|
||||
self.assertIsInstance(form.fields["focal_point_x"].widget, forms.HiddenInput)
|
||||
|
||||
self.assertIn("form_only_field", form.fields)
|
||||
self.assertIs(form.Meta.widgets["form_only_field"], AdminDateTimeInput)
|
||||
1079
env/lib/python3.10/site-packages/wagtail/images/tests/test_image_operations.py
vendored
Normal file
462
env/lib/python3.10/site-packages/wagtail/images/tests/test_jinja2.py
vendored
Normal file
@@ -0,0 +1,462 @@
|
||||
import unittest.mock
|
||||
|
||||
from django.apps import apps
|
||||
from django.template import TemplateSyntaxError, engines
|
||||
from django.test import TestCase
|
||||
|
||||
from wagtail.models import Site
|
||||
|
||||
from .utils import (
|
||||
Image,
|
||||
get_test_bad_image,
|
||||
get_test_image_file,
|
||||
get_test_image_filename,
|
||||
)
|
||||
|
||||
|
||||
class JinjaImagesTestCase(TestCase):
|
||||
maxDiff = None
|
||||
|
||||
def setUp(self):
|
||||
self.engine = engines["jinja2"]
|
||||
|
||||
self.image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
self.bad_image = get_test_bad_image()
|
||||
self.bad_image.save()
|
||||
|
||||
def render(self, string, context=None, request_context=True):
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
# Add a request to the template, to simulate a RequestContext
|
||||
if request_context:
|
||||
site = Site.objects.get(is_default_site=True)
|
||||
request = self.client.get("/test/", HTTP_HOST=site.hostname)
|
||||
context["request"] = request
|
||||
|
||||
template = self.engine.from_string(string)
|
||||
return template.render(context)
|
||||
|
||||
|
||||
class TestImageJinja(JinjaImagesTestCase):
|
||||
def test_image(self):
|
||||
self.assertHTMLEqual(
|
||||
self.render('{{ image(myimage, "width-200") }}', {"myimage": self.image}),
|
||||
'<img alt="Test image" src="{}" width="200" height="150">'.format(
|
||||
get_test_image_filename(self.image, "width-200")
|
||||
),
|
||||
)
|
||||
|
||||
def test_no_image(self):
|
||||
rendered = self.render('{{ image(myimage, "width-2") }}', {"myimage": None})
|
||||
self.assertEqual(rendered, "")
|
||||
|
||||
def test_image_attributes(self):
|
||||
self.assertHTMLEqual(
|
||||
self.render(
|
||||
'{{ image(myimage, "width-200", alt="alternate", class="test") }}',
|
||||
{"myimage": self.image},
|
||||
),
|
||||
'<img alt="alternate" src="{}" width="200" height="150" class="test">'.format(
|
||||
get_test_image_filename(self.image, "width-200")
|
||||
),
|
||||
)
|
||||
|
||||
def test_image_assignment(self):
|
||||
template = (
|
||||
'{% set bg=image(myimage, "width-200") %}'
|
||||
"width: {{ bg.width }}, url: {{ bg.url }}"
|
||||
)
|
||||
output = "width: 200, url: " + get_test_image_filename(self.image, "width-200")
|
||||
self.assertHTMLEqual(self.render(template, {"myimage": self.image}), output)
|
||||
|
||||
def test_image_assignment_render_as_is(self):
|
||||
self.assertHTMLEqual(
|
||||
self.render(
|
||||
'{% set bg=image(myimage, "width-200") %}{{ bg }}',
|
||||
{"myimage": self.image},
|
||||
),
|
||||
'<img alt="Test image" src="{}" width="200" height="150">'.format(
|
||||
get_test_image_filename(self.image, "width-200")
|
||||
),
|
||||
)
|
||||
|
||||
def test_missing_image(self):
|
||||
self.assertHTMLEqual(
|
||||
self.render(
|
||||
'{{ image(myimage, "width-200") }}', {"myimage": self.bad_image}
|
||||
),
|
||||
'<img alt="missing image" src="/media/not-found" width="0" height="0">',
|
||||
)
|
||||
|
||||
def test_invalid_character(self):
|
||||
with self.assertRaisesRegex(
|
||||
TemplateSyntaxError, "filter specs in 'image' tag may only"
|
||||
):
|
||||
self.render('{{ image(myimage, "fill-200×200") }}', {"myimage": self.image})
|
||||
|
||||
def test_custom_default_attrs(self):
|
||||
with unittest.mock.patch.object(
|
||||
apps.get_app_config("wagtailimages"),
|
||||
"default_attrs",
|
||||
new={"decoding": "async", "loading": "lazy"},
|
||||
):
|
||||
self.assertHTMLEqual(
|
||||
self.render(
|
||||
'{{ image(myimage, "width-200") }}', {"myimage": self.bad_image}
|
||||
),
|
||||
'<img alt="missing image" src="/media/not-found" width="0" height="0" decoding="async" loading="lazy">',
|
||||
)
|
||||
|
||||
def test_chaining_filterspecs(self):
|
||||
self.assertHTMLEqual(
|
||||
self.render(
|
||||
'{{ image(myimage, "width-200|jpegquality-40") }}',
|
||||
{"myimage": self.image},
|
||||
),
|
||||
'<img alt="Test image" src="{}" width="200" height="150">'.format(
|
||||
get_test_image_filename(self.image, "width-200.jpegquality-40")
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class TestImageURLJinja(JinjaImagesTestCase):
|
||||
def test_image_url(self):
|
||||
self.assertRegex(
|
||||
self.render(
|
||||
'{{ image_url(myimage, "width-200") }}', {"myimage": self.image}
|
||||
),
|
||||
"/images/.*/width-200/{}".format(self.image.file.name.split("/")[-1]),
|
||||
)
|
||||
|
||||
def test_image_url_custom_view(self):
|
||||
self.assertRegex(
|
||||
self.render(
|
||||
'{{ image_url(myimage, "width-200", "wagtailimages_serve_custom_view") }}',
|
||||
{"myimage": self.image},
|
||||
),
|
||||
"/testimages/custom_view/.*/width-200/{}".format(
|
||||
self.image.file.name.split("/")[-1]
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class TestSrcsetImageJinja(JinjaImagesTestCase):
|
||||
def test_srcset_image(self):
|
||||
filename_200 = get_test_image_filename(self.image, "width-200")
|
||||
filename_400 = get_test_image_filename(self.image, "width-400")
|
||||
rendered = self.render(
|
||||
'{{ srcset_image(myimage, "width-{200,400}", sizes="100vw") }}',
|
||||
{"myimage": self.image},
|
||||
)
|
||||
expected = f"""
|
||||
<img
|
||||
sizes="100vw"
|
||||
src="{filename_200}"
|
||||
srcset="{filename_200} 200w, {filename_400} 400w"
|
||||
alt="Test image"
|
||||
width="200"
|
||||
height="150"
|
||||
>
|
||||
"""
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_no_image(self):
|
||||
rendered = self.render(
|
||||
'{{ srcset_image(myimage, "width-2") }}', {"myimage": None}
|
||||
)
|
||||
self.assertEqual(rendered, "")
|
||||
|
||||
def test_srcset_output_single_image(self):
|
||||
self.assertHTMLEqual(
|
||||
self.render(
|
||||
'{{ srcset_image(myimage, "width-200") }}',
|
||||
{"myimage": self.image},
|
||||
),
|
||||
self.render(
|
||||
'{{ image(myimage, "width-200") }}',
|
||||
{"myimage": self.image},
|
||||
),
|
||||
)
|
||||
|
||||
def test_srcset_image_assignment(self):
|
||||
template = (
|
||||
'{% set bg=srcset_image(myimage, "width-{200,400}") %}'
|
||||
"width: {{ bg.renditions[0].width }}, url: {{ bg.renditions[0].url }} "
|
||||
"width: {{ bg.renditions[1].width }}, url: {{ bg.renditions[1].url }} "
|
||||
)
|
||||
rendered = self.render(template, {"myimage": self.image})
|
||||
expected = f"""
|
||||
width: 200, url: {get_test_image_filename(self.image, "width-200")}
|
||||
width: 400, url: {get_test_image_filename(self.image, "width-400")}
|
||||
"""
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_srcset_image_assignment_render_as_is(self):
|
||||
rendered = self.render(
|
||||
'{% set bg=srcset_image(myimage, "width-{200,400}") %}{{ bg }}',
|
||||
{"myimage": self.image},
|
||||
)
|
||||
expected = self.render(
|
||||
'{{ srcset_image(myimage, "width-{200,400}") }}',
|
||||
{"myimage": self.image},
|
||||
)
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_missing_srcset_image(self):
|
||||
rendered = self.render(
|
||||
'{{ srcset_image(myimage, "width-{200,400}", sizes="100vw") }}',
|
||||
{"myimage": self.bad_image},
|
||||
)
|
||||
expected = """
|
||||
<img
|
||||
sizes="100vw"
|
||||
src="/media/not-found"
|
||||
srcset="/media/not-found 0w, /media/not-found 0w"
|
||||
alt="missing image"
|
||||
width="0"
|
||||
height="0"
|
||||
>
|
||||
"""
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_invalid_character(self):
|
||||
with self.assertRaisesRegex(
|
||||
TemplateSyntaxError, "filter specs in 'srcset_image' tag may only"
|
||||
):
|
||||
self.render(
|
||||
'{{ srcset_image(myimage, "fill-{20×20,40×40}", sizes="100vw") }}',
|
||||
{"myimage": self.image},
|
||||
)
|
||||
|
||||
def test_custom_default_attrs(self):
|
||||
with unittest.mock.patch.object(
|
||||
apps.get_app_config("wagtailimages"),
|
||||
"default_attrs",
|
||||
new={"decoding": "async", "loading": "lazy"},
|
||||
):
|
||||
rendered = self.render(
|
||||
'{{ srcset_image(myimage, "width-{20,40}", sizes="100vw") }}',
|
||||
{"myimage": self.bad_image},
|
||||
)
|
||||
expected = """
|
||||
<img
|
||||
sizes="100vw"
|
||||
src="/media/not-found"
|
||||
srcset="/media/not-found 0w, /media/not-found 0w"
|
||||
alt="missing image"
|
||||
width="0"
|
||||
height="0"
|
||||
decoding="async"
|
||||
loading="lazy"
|
||||
>
|
||||
"""
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_chaining_filterspecs(self):
|
||||
filenames = [
|
||||
get_test_image_filename(self.image, "width-200.jpegquality-40"),
|
||||
get_test_image_filename(self.image, "width-400.jpegquality-40"),
|
||||
]
|
||||
rendered = self.render(
|
||||
'{{ srcset_image(myimage, "width-{200,400}|jpegquality-40", sizes="100vw") }}',
|
||||
{"myimage": self.image},
|
||||
)
|
||||
expected = f"""
|
||||
<img
|
||||
sizes="100vw"
|
||||
src="{filenames[0]}"
|
||||
srcset="{filenames[0]} 200w, {filenames[1]} 400w"
|
||||
alt="Test image"
|
||||
width="200"
|
||||
height="150"
|
||||
>
|
||||
"""
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
|
||||
class TestPictureJinja(JinjaImagesTestCase):
|
||||
def test_picture_formats_multi_sizes(self):
|
||||
filenames = [
|
||||
get_test_image_filename(self.image, "width-200.format-jpeg"),
|
||||
get_test_image_filename(self.image, "width-400.format-jpeg"),
|
||||
get_test_image_filename(self.image, "width-200.format-webp"),
|
||||
get_test_image_filename(self.image, "width-400.format-webp"),
|
||||
get_test_image_filename(self.image, "width-200.format-gif"),
|
||||
get_test_image_filename(self.image, "width-400.format-gif"),
|
||||
]
|
||||
|
||||
rendered = self.render(
|
||||
'{{ picture(myimage, "width-{200,400}|format-{jpeg,webp,gif}", sizes="100vw") }}',
|
||||
{"myimage": self.image},
|
||||
)
|
||||
expected = f"""
|
||||
<picture>
|
||||
<source srcset="{filenames[2]} 200w, {filenames[3]} 400w" sizes="100vw" type="image/webp">
|
||||
<source srcset="{filenames[0]} 200w, {filenames[1]} 400w" sizes="100vw" type="image/jpeg">
|
||||
<img
|
||||
sizes="100vw"
|
||||
src="{filenames[4]}"
|
||||
srcset="{filenames[4]} 200w, {filenames[5]} 400w"
|
||||
alt="Test image"
|
||||
width="200"
|
||||
height="150"
|
||||
>
|
||||
</picture>
|
||||
"""
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_picture_formats_only(self):
|
||||
filename_jpeg = get_test_image_filename(self.image, "format-jpeg")
|
||||
filename_webp = get_test_image_filename(self.image, "format-webp")
|
||||
|
||||
rendered = self.render(
|
||||
'{{ picture(myimage, "format-{jpeg,webp}") }}',
|
||||
{"myimage": self.image},
|
||||
)
|
||||
expected = f"""
|
||||
<picture>
|
||||
<source srcset="{filename_webp}" type="image/webp">
|
||||
<img
|
||||
src="{filename_jpeg}"
|
||||
alt="Test image"
|
||||
width="640"
|
||||
height="480"
|
||||
>
|
||||
</picture>
|
||||
"""
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_picture_sizes_only(self):
|
||||
rendered = self.render(
|
||||
'{{ picture(myimage, "width-{200,400}", sizes="100vw") }}',
|
||||
{"myimage": self.image},
|
||||
)
|
||||
expected = self.render(
|
||||
'<picture>{{ srcset_image(myimage, "width-{200,400}", sizes="100vw") }}</picture>',
|
||||
{"myimage": self.image},
|
||||
)
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_picture_single_format(self):
|
||||
rendered = self.render(
|
||||
'{{ picture(myimage, "format-jpeg") }}',
|
||||
{"myimage": self.image},
|
||||
)
|
||||
expected = self.render(
|
||||
'<picture>{{ image(myimage, "format-jpeg") }}</picture>',
|
||||
{"myimage": self.image},
|
||||
)
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_no_image(self):
|
||||
rendered = self.render('{{ picture(myimage, "width-2") }}', {"myimage": None})
|
||||
self.assertEqual(rendered, "")
|
||||
|
||||
def test_picture_assignment(self):
|
||||
template = (
|
||||
'{% set bg=picture(myimage, "width-{200,400}|format-{jpeg,webp}") %}'
|
||||
"width: {{ bg.formats['jpeg'][0].width }}, url: {{ bg.formats['jpeg'][0].url }} "
|
||||
"width: {{ bg.formats['jpeg'][1].width }}, url: {{ bg.formats['jpeg'][1].url }} "
|
||||
"width: {{ bg.formats['webp'][0].width }}, url: {{ bg.formats['webp'][0].url }} "
|
||||
"width: {{ bg.formats['webp'][1].width }}, url: {{ bg.formats['webp'][1].url }} "
|
||||
)
|
||||
rendered = self.render(template, {"myimage": self.image})
|
||||
expected = f"""
|
||||
width: 200, url: {get_test_image_filename(self.image, "width-200.format-jpeg")}
|
||||
width: 400, url: {get_test_image_filename(self.image, "width-400.format-jpeg")}
|
||||
width: 200, url: {get_test_image_filename(self.image, "width-200.format-webp")}
|
||||
width: 400, url: {get_test_image_filename(self.image, "width-400.format-webp")}
|
||||
"""
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_picture_assignment_render_as_is(self):
|
||||
rendered = self.render(
|
||||
'{% set bg=picture(myimage, "width-{200,400}|format-{jpeg,webp,gif}", sizes="100vw") %}{{ bg }}',
|
||||
{"myimage": self.image},
|
||||
)
|
||||
expected = self.render(
|
||||
'{{ picture(myimage, "width-{200,400}|format-{jpeg,webp,gif}", sizes="100vw") }}',
|
||||
{"myimage": self.image},
|
||||
)
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_missing_picture(self):
|
||||
rendered = self.render(
|
||||
'{{ picture(myimage, "format-{jpeg,webp}") }}',
|
||||
{"myimage": self.bad_image},
|
||||
)
|
||||
expected = """
|
||||
<picture>
|
||||
<source srcset="/media/not-found" type="image/webp">
|
||||
<img
|
||||
src="/media/not-found"
|
||||
alt="missing image"
|
||||
width="0"
|
||||
height="0"
|
||||
>
|
||||
</picture>
|
||||
"""
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_invalid_character(self):
|
||||
with self.assertRaisesRegex(
|
||||
TemplateSyntaxError, "filter specs in 'picture' tag may only"
|
||||
):
|
||||
self.render(
|
||||
'{{ picture(myimage, "fill-{20×20,40×40}", sizes="100vw") }}',
|
||||
{"myimage": self.image},
|
||||
)
|
||||
|
||||
def test_custom_default_attrs(self):
|
||||
with unittest.mock.patch.object(
|
||||
apps.get_app_config("wagtailimages"),
|
||||
"default_attrs",
|
||||
new={"decoding": "async", "loading": "lazy"},
|
||||
):
|
||||
rendered = self.render(
|
||||
'{{ picture(myimage, "format-{jpeg,webp}") }}',
|
||||
{"myimage": self.bad_image},
|
||||
)
|
||||
expected = """
|
||||
<picture>
|
||||
<source srcset="/media/not-found" type="image/webp">
|
||||
<img
|
||||
src="/media/not-found"
|
||||
alt="missing image"
|
||||
width="0"
|
||||
height="0"
|
||||
decoding="async"
|
||||
loading="lazy"
|
||||
>
|
||||
</picture>
|
||||
"""
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_chaining_filterspecs(self):
|
||||
filename_jpeg = get_test_image_filename(
|
||||
self.image, "format-jpeg.jpegquality-40.webpquality-40"
|
||||
)
|
||||
filename_webp = get_test_image_filename(
|
||||
self.image, "format-webp.jpegquality-40.webpquality-40"
|
||||
)
|
||||
rendered = self.render(
|
||||
'{{ picture(myimage, "format-{jpeg,webp}|jpegquality-40|webpquality-40") }}',
|
||||
{"myimage": self.image},
|
||||
)
|
||||
expected = f"""
|
||||
<picture>
|
||||
<source srcset="{filename_webp}" type="image/webp">
|
||||
<img
|
||||
src="{filename_jpeg}"
|
||||
alt="Test image"
|
||||
width="640"
|
||||
height="480"
|
||||
>
|
||||
</picture>
|
||||
"""
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
140
env/lib/python3.10/site-packages/wagtail/images/tests/test_management_commands.py
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
import re
|
||||
import warnings
|
||||
from io import StringIO
|
||||
|
||||
from django.core import management
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
from ..management.commands.wagtail_update_image_renditions import progress_bar
|
||||
from .utils import Image, get_test_image_file
|
||||
|
||||
# note .utils.Image already does get_image_model()
|
||||
Rendition = Image.get_rendition_model()
|
||||
|
||||
|
||||
class TestUpdateImageRenditions(TestCase):
|
||||
REAESC = re.compile(r"\x1b[^m]*m")
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(filename="test_image.png", colour="white"),
|
||||
)
|
||||
|
||||
cls.rendition = Rendition.objects.create(
|
||||
image=cls.image,
|
||||
filter_spec="original",
|
||||
width=1000,
|
||||
height=1000,
|
||||
file=get_test_image_file(
|
||||
filename="test_rendition.png", colour="white", size=(1000, 1000)
|
||||
),
|
||||
)
|
||||
|
||||
def delete_renditions(self):
|
||||
renditions = Rendition.objects.all()
|
||||
for rendition in renditions:
|
||||
try:
|
||||
rendition_image = rendition.image
|
||||
rendition.delete()
|
||||
except Exception: # noqa: BLE001
|
||||
warnings.warn(f"Could not delete rendition for {rendition_image}")
|
||||
|
||||
def run_command(self, **options):
|
||||
output = StringIO()
|
||||
management.call_command(
|
||||
"wagtail_update_image_renditions", stdout=output, **options
|
||||
)
|
||||
output.seek(0)
|
||||
|
||||
return output
|
||||
|
||||
def test_progress_bar(self):
|
||||
total_rendition = 10
|
||||
out = StringIO()
|
||||
for current in range(1, total_rendition + 1):
|
||||
progress_bar_output = progress_bar(current, total_rendition)[0]
|
||||
out.write(progress_bar_output)
|
||||
out.seek(0)
|
||||
expected_output = "".join(
|
||||
[
|
||||
"Progress: [----> ] 10%",
|
||||
"Progress: [---------> ] 20%",
|
||||
"Progress: [--------------> ] 30%",
|
||||
"Progress: [-------------------> ] 40%",
|
||||
"Progress: [------------------------> ] 50%",
|
||||
"Progress: [-----------------------------> ] 60%",
|
||||
"Progress: [----------------------------------> ] 70%",
|
||||
"Progress: [---------------------------------------> ] 80%",
|
||||
"Progress: [--------------------------------------------> ] 90%",
|
||||
"Progress: [------------------------------------------------->] 100%",
|
||||
]
|
||||
)
|
||||
self.assertIn(expected_output, out.getvalue())
|
||||
|
||||
def test_exits_early_for_no_renditions(self):
|
||||
self.delete_renditions()
|
||||
# checking when command is called without any arguments
|
||||
output = self.run_command()
|
||||
output_string = self.REAESC.sub("", output.read())
|
||||
self.assertEqual(output_string, "No image renditions found.\n")
|
||||
|
||||
# checking when command is called with '--purge-only'
|
||||
output = self.run_command(purge_only=True)
|
||||
output_string = self.REAESC.sub("", output.read())
|
||||
self.assertEqual(output_string, "No image renditions found.\n")
|
||||
|
||||
def test_image_renditions(self):
|
||||
renditions = Rendition.objects.all()
|
||||
total_renditions = len(renditions)
|
||||
output = self.run_command()
|
||||
output_string = self.REAESC.sub("", output.read())
|
||||
# checking if the number of renditions regenerated equal total_renditions
|
||||
self.assertEqual(
|
||||
output_string,
|
||||
f"Regenerating {total_renditions} rendition(s)\n"
|
||||
f"Progress: [------------------------------------------------->] 100%\n"
|
||||
f"Successfully processed {total_renditions} rendition(s)\n",
|
||||
)
|
||||
|
||||
# checking if the number of renditions now equal total_renditions
|
||||
renditions_now = Rendition.objects.all()
|
||||
total_renditions_now = len(renditions_now)
|
||||
self.assertEqual(total_renditions_now, total_renditions)
|
||||
|
||||
def test_image_renditions_with_purge_only(self):
|
||||
renditions = Rendition.objects.all()
|
||||
total_renditions = len(renditions)
|
||||
output = self.run_command(purge_only=True)
|
||||
output_string = self.REAESC.sub("", output.read())
|
||||
# checking if the number of renditions purged equal total_renditions
|
||||
self.assertEqual(
|
||||
output_string,
|
||||
f"Purging {total_renditions} rendition(s)\n"
|
||||
f"Progress: [------------------------------------------------->] 100%\n"
|
||||
f"Successfully processed {total_renditions} rendition(s)\n",
|
||||
)
|
||||
|
||||
# checking if the number of renditions now equal 0
|
||||
renditions_now = Rendition.objects.all()
|
||||
total_renditions_now = len(renditions_now)
|
||||
self.assertEqual(total_renditions_now, 0)
|
||||
|
||||
@override_settings(
|
||||
CACHES={"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}}
|
||||
)
|
||||
def test_image_renditions_with_cache(self):
|
||||
total_renditions = Rendition.objects.count()
|
||||
output = self.run_command()
|
||||
output_string = self.REAESC.sub("", output.read())
|
||||
self.assertIn(
|
||||
f"Successfully processed {total_renditions} rendition(s)\n", output_string
|
||||
)
|
||||
|
||||
# Run the command again with a warmed cache
|
||||
output = self.run_command()
|
||||
output_string = self.REAESC.sub("", output.read())
|
||||
self.assertIn(
|
||||
f"Successfully processed {total_renditions} rendition(s)\n", output_string
|
||||
)
|
||||
1388
env/lib/python3.10/site-packages/wagtail/images/tests/test_models.py
vendored
Normal file
156
env/lib/python3.10/site-packages/wagtail/images/tests/test_rich_text.py
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse_lazy
|
||||
|
||||
from wagtail.fields import RichTextField
|
||||
from wagtail.images.rich_text import ImageEmbedHandler as FrontendImageEmbedHandler
|
||||
from wagtail.images.rich_text.editor_html import (
|
||||
ImageEmbedHandler as EditorHtmlImageEmbedHandler,
|
||||
)
|
||||
from wagtail.rich_text.feature_registry import FeatureRegistry
|
||||
from wagtail.test.utils import WagtailTestUtils
|
||||
|
||||
from .utils import Image, get_test_image_file
|
||||
|
||||
|
||||
class TestEditorHtmlImageEmbedHandler(WagtailTestUtils, TestCase):
|
||||
def test_get_db_attributes(self):
|
||||
soup = self.get_soup(
|
||||
'<b data-id="test-id" data-format="test-format" data-alt="test-alt">foo</b>',
|
||||
)
|
||||
tag = soup.b
|
||||
result = EditorHtmlImageEmbedHandler.get_db_attributes(tag)
|
||||
self.assertEqual(
|
||||
result,
|
||||
{
|
||||
"alt": "test-alt",
|
||||
"id": "test-id",
|
||||
"format": "test-format",
|
||||
},
|
||||
)
|
||||
|
||||
def test_expand_db_attributes_for_editor(self):
|
||||
Image.objects.create(id=1, title="Test", file=get_test_image_file())
|
||||
result = EditorHtmlImageEmbedHandler.expand_db_attributes(
|
||||
{
|
||||
"id": 1,
|
||||
"alt": "test-alt",
|
||||
"format": "left",
|
||||
}
|
||||
)
|
||||
self.assertTagInHTML(
|
||||
(
|
||||
'<img data-embedtype="image" data-id="1" data-format="left" '
|
||||
'data-alt="test-alt" class="richtext-image left" />'
|
||||
),
|
||||
result,
|
||||
allow_extra_attrs=True,
|
||||
)
|
||||
|
||||
def test_expand_db_attributes_for_editor_nonexistent_image(self):
|
||||
self.assertEqual(
|
||||
EditorHtmlImageEmbedHandler.expand_db_attributes({"id": 0}), '<img alt="">'
|
||||
)
|
||||
|
||||
def test_expand_db_attributes_for_editor_escapes_alt_text(self):
|
||||
Image.objects.create(id=1, title="Test", file=get_test_image_file())
|
||||
result = EditorHtmlImageEmbedHandler.expand_db_attributes(
|
||||
{
|
||||
"id": 1,
|
||||
"alt": 'Arthur "two sheds" Jackson',
|
||||
"format": "left",
|
||||
}
|
||||
)
|
||||
|
||||
self.assertTagInHTML(
|
||||
(
|
||||
'<img data-embedtype="image" data-id="1" data-format="left" '
|
||||
'data-alt="Arthur "two sheds" Jackson" class="richtext-image left" />'
|
||||
),
|
||||
result,
|
||||
allow_extra_attrs=True,
|
||||
)
|
||||
|
||||
self.assertIn('alt="Arthur "two sheds" Jackson"', result)
|
||||
|
||||
def test_expand_db_attributes_for_editor_with_missing_alt(self):
|
||||
Image.objects.create(id=1, title="Test", file=get_test_image_file())
|
||||
result = EditorHtmlImageEmbedHandler.expand_db_attributes(
|
||||
{
|
||||
"id": 1,
|
||||
"format": "left",
|
||||
}
|
||||
)
|
||||
self.assertTagInHTML(
|
||||
(
|
||||
'<img data-embedtype="image" data-id="1" data-format="left" data-alt="" '
|
||||
'class="richtext-image left" />'
|
||||
),
|
||||
result,
|
||||
allow_extra_attrs=True,
|
||||
)
|
||||
|
||||
|
||||
class TestFrontendImageEmbedHandler(WagtailTestUtils, TestCase):
|
||||
def test_expand_db_attributes_for_frontend(self):
|
||||
Image.objects.create(id=1, title="Test", file=get_test_image_file())
|
||||
result = FrontendImageEmbedHandler.expand_db_attributes(
|
||||
{
|
||||
"id": 1,
|
||||
"alt": "test-alt",
|
||||
"format": "left",
|
||||
}
|
||||
)
|
||||
self.assertTagInHTML(
|
||||
'<img class="richtext-image left" />', result, allow_extra_attrs=True
|
||||
)
|
||||
|
||||
def test_expand_db_attributes_for_frontend_with_nonexistent_image(self):
|
||||
result = FrontendImageEmbedHandler.expand_db_attributes({"id": 0})
|
||||
self.assertEqual(result, '<img alt="">')
|
||||
|
||||
def test_expand_db_attributes_for_frontend_escapes_alt_text(self):
|
||||
Image.objects.create(id=1, title="Test", file=get_test_image_file())
|
||||
result = FrontendImageEmbedHandler.expand_db_attributes(
|
||||
{
|
||||
"id": 1,
|
||||
"alt": 'Arthur "two sheds" Jackson',
|
||||
"format": "left",
|
||||
}
|
||||
)
|
||||
self.assertIn('alt="Arthur "two sheds" Jackson"', result)
|
||||
|
||||
def test_expand_db_attributes_for_frontend_with_missing_alt(self):
|
||||
Image.objects.create(id=1, title="Test", file=get_test_image_file())
|
||||
result = FrontendImageEmbedHandler.expand_db_attributes(
|
||||
{
|
||||
"id": 1,
|
||||
"format": "left",
|
||||
}
|
||||
)
|
||||
self.assertTagInHTML(
|
||||
'<img class="richtext-image left" alt="" />', result, allow_extra_attrs=True
|
||||
)
|
||||
|
||||
|
||||
class TestExtractReferencesWithImage(WagtailTestUtils, TestCase):
|
||||
def test_extract_references(self):
|
||||
self.assertEqual(
|
||||
list(
|
||||
RichTextField().extract_references(
|
||||
'<embed alt="Olivia Ava" embedtype="image" format="left" id="52"/>'
|
||||
)
|
||||
),
|
||||
[(Image, "52", "", "")],
|
||||
)
|
||||
|
||||
|
||||
class TestEntityFeatureChooserUrls(TestCase):
|
||||
def test_chooser_urls_exist(self):
|
||||
features = FeatureRegistry()
|
||||
image = features.get_editor_plugin("draftail", "image")
|
||||
|
||||
self.assertIsNotNone(image.data.get("chooserUrls"))
|
||||
self.assertEqual(
|
||||
image.data["chooserUrls"]["imageChooser"],
|
||||
reverse_lazy("wagtailimages_chooser:choose"),
|
||||
)
|
||||
64
env/lib/python3.10/site-packages/wagtail/images/tests/test_shortcuts.py
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from wagtail.images.models import Filter
|
||||
from wagtail.images.shortcuts import (
|
||||
get_rendition_or_not_found,
|
||||
get_renditions_or_not_found,
|
||||
)
|
||||
|
||||
from .utils import Image, get_test_image_file
|
||||
|
||||
|
||||
class TestShortcuts(TestCase):
|
||||
fixtures = ["test.json"]
|
||||
|
||||
def test_fallback_to_not_found(self):
|
||||
bad_image = Image.objects.get(id=1)
|
||||
good_image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
rendition = get_rendition_or_not_found(good_image, "width-400")
|
||||
self.assertEqual(rendition.width, 400)
|
||||
|
||||
rendition = get_rendition_or_not_found(bad_image, "width-400")
|
||||
self.assertEqual(rendition.file.name, "not-found")
|
||||
|
||||
def test_multiple_fallback_to_not_found(self):
|
||||
bad_image = Image.objects.get(id=1)
|
||||
good_image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
renditions = get_renditions_or_not_found(good_image, ("width-200", "width-400"))
|
||||
self.assertEqual(tuple(renditions.keys()), ("width-200", "width-400"))
|
||||
self.assertEqual(renditions["width-200"].width, 200)
|
||||
self.assertEqual(renditions["width-400"].width, 400)
|
||||
|
||||
renditions = get_renditions_or_not_found(bad_image, ("width-200", "width-400"))
|
||||
self.assertEqual(tuple(renditions.keys()), ("width-200", "width-400"))
|
||||
self.assertEqual(renditions["width-200"].file.name, "not-found")
|
||||
self.assertEqual(renditions["width-400"].file.name, "not-found")
|
||||
|
||||
def test_multiple_fallback_to_not_found_with_filters(self):
|
||||
bad_image = Image.objects.get(id=1)
|
||||
good_image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
renditions = get_renditions_or_not_found(
|
||||
good_image, (Filter("width-200"), Filter("width-400"))
|
||||
)
|
||||
self.assertEqual(tuple(renditions.keys()), ("width-200", "width-400"))
|
||||
self.assertEqual(renditions["width-200"].width, 200)
|
||||
self.assertEqual(renditions["width-400"].width, 400)
|
||||
|
||||
renditions = get_renditions_or_not_found(
|
||||
bad_image, (Filter("width-200"), Filter("width-400"))
|
||||
)
|
||||
self.assertEqual(tuple(renditions.keys()), ("width-200", "width-400"))
|
||||
self.assertEqual(renditions["width-200"].file.name, "not-found")
|
||||
self.assertEqual(renditions["width-400"].file.name, "not-found")
|
||||
95
env/lib/python3.10/site-packages/wagtail/images/tests/test_signal_handlers.py
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
from django.db import transaction
|
||||
from django.test import TestCase, TransactionTestCase, override_settings
|
||||
|
||||
from wagtail.images import get_image_model, signal_handlers
|
||||
from wagtail.images.tests.utils import get_test_image_file
|
||||
from wagtail.models import Collection
|
||||
|
||||
from .utils import Image
|
||||
|
||||
|
||||
class TestFilesDeletedForDefaultModels(TransactionTestCase):
|
||||
"""
|
||||
Because we expect file deletion to only happen once a transaction is
|
||||
successfully committed, we must run these tests using TransactionTestCase
|
||||
per the following documentation:
|
||||
|
||||
Django's TestCase class wraps each test in a transaction and rolls back that
|
||||
transaction after each test, in order to provide test isolation. This means
|
||||
that no transaction is ever actually committed, thus your on_commit()
|
||||
callbacks will never be run. If you need to test the results of an
|
||||
on_commit() callback, use a TransactionTestCase instead.
|
||||
https://docs.djangoproject.com/en/1.10/topics/db/transactions/#use-in-tests
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
# Required to create root collection because the TransactionTestCase
|
||||
# does not make initial data loaded in migrations available and
|
||||
# serialized_rollback=True causes other problems in the test suite.
|
||||
# ref: https://docs.djangoproject.com/en/1.10/topics/testing/overview/#rollback-emulation
|
||||
Collection.objects.get_or_create(
|
||||
name="Root",
|
||||
path="0001",
|
||||
depth=1,
|
||||
numchild=0,
|
||||
)
|
||||
|
||||
def test_image_file_deleted_oncommit(self):
|
||||
with transaction.atomic():
|
||||
image = get_image_model().objects.create(
|
||||
title="Test Image", file=get_test_image_file()
|
||||
)
|
||||
filename = image.file.name
|
||||
self.assertTrue(image.file.storage.exists(filename))
|
||||
image.delete()
|
||||
self.assertTrue(image.file.storage.exists(filename))
|
||||
self.assertFalse(image.file.storage.exists(filename))
|
||||
|
||||
def test_rendition_file_deleted_oncommit(self):
|
||||
with transaction.atomic():
|
||||
image = get_image_model().objects.create(
|
||||
title="Test Image", file=get_test_image_file()
|
||||
)
|
||||
rendition = image.get_rendition("original")
|
||||
filename = rendition.file.name
|
||||
self.assertTrue(rendition.file.storage.exists(filename))
|
||||
rendition.delete()
|
||||
self.assertTrue(rendition.file.storage.exists(filename))
|
||||
self.assertFalse(rendition.file.storage.exists(filename))
|
||||
|
||||
|
||||
@override_settings(WAGTAILIMAGES_IMAGE_MODEL="tests.CustomImage")
|
||||
class TestFilesDeletedForCustomModels(TestFilesDeletedForDefaultModels):
|
||||
def setUp(self):
|
||||
# Required to create root collection because the TransactionTestCase
|
||||
# does not make initial data loaded in migrations available and
|
||||
# serialized_rollback=True causes other problems in the test suite.
|
||||
# ref: https://docs.djangoproject.com/en/1.10/topics/testing/overview/#rollback-emulation
|
||||
Collection.objects.get_or_create(
|
||||
name="Root",
|
||||
path="0001",
|
||||
depth=1,
|
||||
numchild=0,
|
||||
)
|
||||
|
||||
#: Sadly signal receivers only get connected when starting django.
|
||||
#: We will re-attach them here to mimic the django startup behaviour
|
||||
#: and get the signals connected to our custom model..
|
||||
signal_handlers.register_signal_handlers()
|
||||
|
||||
def test_image_model(self):
|
||||
cls = get_image_model()
|
||||
self.assertEqual(f"{cls._meta.app_label}.{cls.__name__}", "tests.CustomImage")
|
||||
|
||||
|
||||
@override_settings(WAGTAILIMAGES_FEATURE_DETECTION_ENABLED=True)
|
||||
class TestRawForPreSaveImageFeatureDetection(TestCase):
|
||||
fixtures = ["test.json"]
|
||||
|
||||
# just to test the file is from a fixture doesn't actually exists.
|
||||
# raw check in pre_save_image_feature_detection skips on the provided condition of this test
|
||||
# hence avoiding an error
|
||||
|
||||
def test_image_does_not_exist(self):
|
||||
bad_image = Image.objects.get(pk=1)
|
||||
self.assertFalse(bad_image.file.storage.exists(bad_image.file.name))
|
||||
136
env/lib/python3.10/site-packages/wagtail/images/tests/test_site_summary.py
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
from django.contrib.auth.models import Group, Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from wagtail.images.tests.utils import Image
|
||||
from wagtail.images.wagtail_hooks import ImagesSummaryItem
|
||||
from wagtail.models import Collection, GroupCollectionPermission, Site
|
||||
from wagtail.test.utils import WagtailTestUtils
|
||||
|
||||
|
||||
class TestImagesSummary(WagtailTestUtils, TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
# Permissions
|
||||
image_content_type = ContentType.objects.get_for_model(Image)
|
||||
add_image_permission = Permission.objects.get(
|
||||
content_type=image_content_type, codename="add_image"
|
||||
)
|
||||
change_image_permission = Permission.objects.get(
|
||||
content_type=image_content_type, codename="change_image"
|
||||
)
|
||||
choose_image_permission = Permission.objects.get(
|
||||
content_type=image_content_type, codename="choose_image"
|
||||
)
|
||||
|
||||
# Collections
|
||||
self.root_collection = Collection.get_first_root_node()
|
||||
self.birds_collection = self.root_collection.add_child(name="Birds")
|
||||
|
||||
# Groups
|
||||
image_changers_group = Group.objects.create(name="Image changers")
|
||||
GroupCollectionPermission.objects.create(
|
||||
group=image_changers_group,
|
||||
collection=self.root_collection,
|
||||
permission=change_image_permission,
|
||||
)
|
||||
|
||||
bird_adders_group = Group.objects.create(name="Bird adders")
|
||||
GroupCollectionPermission.objects.create(
|
||||
group=bird_adders_group,
|
||||
collection=self.birds_collection,
|
||||
permission=add_image_permission,
|
||||
)
|
||||
|
||||
bird_choosers_group = Group.objects.create(name="Bird choosers")
|
||||
GroupCollectionPermission.objects.create(
|
||||
group=bird_choosers_group,
|
||||
collection=self.birds_collection,
|
||||
permission=choose_image_permission,
|
||||
)
|
||||
|
||||
# Users
|
||||
self.superuser = self.create_superuser(
|
||||
"superuser", "superuser@example.com", "password"
|
||||
)
|
||||
|
||||
# a user with add_image permission on birds via the bird_adders group
|
||||
self.bird_adder = self.create_user(
|
||||
"birdadder", "birdadder@example.com", "password"
|
||||
)
|
||||
self.bird_adder.groups.add(bird_adders_group)
|
||||
|
||||
# a user with choose_image permission on birds via the bird_choosers group
|
||||
self.bird_chooser = self.create_user(
|
||||
"birdchooser", "birdchooser@example.com", "password"
|
||||
)
|
||||
self.bird_chooser.groups.add(bird_choosers_group)
|
||||
|
||||
# Images
|
||||
|
||||
# an image in the root owned by 'birdadder'
|
||||
self.changer_image = Image.objects.create(
|
||||
title="birdadder's image",
|
||||
collection=self.root_collection,
|
||||
uploaded_by_user=self.bird_adder,
|
||||
width=1,
|
||||
height=1,
|
||||
)
|
||||
|
||||
# an image in birds owned by 'birdadder'
|
||||
self.changer_bird = Image.objects.create(
|
||||
title="birdadder's bird",
|
||||
collection=self.birds_collection,
|
||||
uploaded_by_user=self.bird_adder,
|
||||
width=2,
|
||||
height=2,
|
||||
)
|
||||
|
||||
# an image in birds owned by 'birdadder'
|
||||
self.adder_bird = Image.objects.create(
|
||||
title="birdadder's bird",
|
||||
collection=self.birds_collection,
|
||||
uploaded_by_user=self.bird_adder,
|
||||
width=3,
|
||||
height=3,
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
self.login(self.superuser)
|
||||
|
||||
def get_request(self):
|
||||
return self.client.get(reverse("wagtailadmin_home")).wsgi_request
|
||||
|
||||
def assertSummaryContains(self, content):
|
||||
summary = ImagesSummaryItem(self.get_request()).render_html()
|
||||
self.assertIn(content, summary)
|
||||
|
||||
def test_site_name_is_shown(self):
|
||||
self.assertEqual(Site.objects.count(), 1)
|
||||
site = Site.objects.first()
|
||||
self.assertSummaryContains(site.site_name)
|
||||
|
||||
def test_user_with_permissions_is_shown_panel(self):
|
||||
self.assertTrue(ImagesSummaryItem(self.get_request()).is_shown())
|
||||
|
||||
def test_user_with_no_permissions_is_not_shown_panel(self):
|
||||
self.superuser.is_superuser = False
|
||||
self.superuser.user_permissions.add(
|
||||
Permission.objects.get(
|
||||
content_type__app_label="wagtailadmin", codename="access_admin"
|
||||
)
|
||||
)
|
||||
self.superuser.save()
|
||||
self.assertFalse(ImagesSummaryItem(self.get_request()).is_shown())
|
||||
|
||||
def test_user_sees_proper_image_count(self):
|
||||
cases = (
|
||||
(self.superuser, "<span>3</span> Images"),
|
||||
(self.bird_adder, "<span>2</span> Images"),
|
||||
(self.bird_chooser, "<span>2</span> Images"),
|
||||
)
|
||||
for user, content in cases:
|
||||
with self.subTest(user=user):
|
||||
self.login(user)
|
||||
self.assertSummaryContains(content)
|
||||
480
env/lib/python3.10/site-packages/wagtail/images/tests/test_templatetags.py
vendored
Normal file
@@ -0,0 +1,480 @@
|
||||
from django.template import Context, Engine, TemplateSyntaxError, Variable
|
||||
from django.test import TestCase
|
||||
|
||||
from wagtail.images.models import Image, Rendition
|
||||
from wagtail.images.templatetags.wagtailimages_tags import ImageNode
|
||||
from wagtail.images.tests.utils import (
|
||||
get_test_bad_image,
|
||||
get_test_image_file,
|
||||
get_test_image_file_svg,
|
||||
get_test_image_filename,
|
||||
)
|
||||
|
||||
LIBRARIES = {
|
||||
"wagtailimages_tags": "wagtail.images.templatetags.wagtailimages_tags",
|
||||
}
|
||||
|
||||
|
||||
class ImageNodeTestCase(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
# Create an image for running tests on
|
||||
cls.image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
cls.svg_image = Image.objects.create(
|
||||
title="Test SVG image",
|
||||
file=get_test_image_file_svg(),
|
||||
)
|
||||
|
||||
def test_render_valid_image_to_string(self):
|
||||
"""
|
||||
Tests that an ImageNode with a valid image renders an img tag
|
||||
"""
|
||||
context = {"image": self.image}
|
||||
node = ImageNode(Variable("image"), ["original"])
|
||||
|
||||
rendered = node.render(context)
|
||||
|
||||
self.assertIn('<img alt="Test image"', rendered)
|
||||
|
||||
def test_render_none_to_string(self):
|
||||
"""
|
||||
Tests that an ImageNode without image renders an empty string
|
||||
"""
|
||||
context = {"image": None}
|
||||
node = ImageNode(Variable("image"), ["original"])
|
||||
|
||||
rendered = node.render(context)
|
||||
|
||||
self.assertEqual(rendered, "")
|
||||
|
||||
def test_render_valid_image_as_context_variable(self):
|
||||
"""
|
||||
Tests that an ImageNode with a valid image and a context variable name
|
||||
renders an empty string and puts a rendition in the context variable
|
||||
"""
|
||||
context = {"image": self.image, "image_node": "fake value"}
|
||||
node = ImageNode(Variable("image"), ["original"], "image_node")
|
||||
|
||||
rendered = node.render(context)
|
||||
|
||||
self.assertEqual(rendered, "")
|
||||
self.assertIsInstance(context["image_node"], Rendition)
|
||||
|
||||
def test_render_none_as_context_variable(self):
|
||||
"""
|
||||
Tests that an ImageNode without an image and a context variable name
|
||||
renders an empty string and puts None in the context variable
|
||||
"""
|
||||
context = {"image": None, "image_node": "fake value"}
|
||||
node = ImageNode(Variable("image"), ["original"], "image_node")
|
||||
|
||||
rendered = node.render(context)
|
||||
|
||||
self.assertEqual(rendered, "")
|
||||
self.assertIsNone(context["image_node"])
|
||||
|
||||
def test_filters_preserve_svg(self):
|
||||
"""
|
||||
If the image is an SVG, and we set the preserve_svg parameter of ImageNode
|
||||
to True, we should only use filters that don't require rasterisation (at this
|
||||
time, resize and crop operations only).
|
||||
"""
|
||||
params = [
|
||||
(self.svg_image, ["original"], "original"),
|
||||
(self.svg_image, ["fill-400x400", "bgcolor-000"], "fill-400x400"),
|
||||
(
|
||||
self.svg_image,
|
||||
["fill-400x400", "format-webp", "webpquality-50"],
|
||||
"fill-400x400",
|
||||
),
|
||||
(self.image, ["fill-400x400", "bgcolor-000"], "fill-400x400|bgcolor-000"),
|
||||
(self.image, ["fill-400x400", "format-webp"], "fill-400x400|format-webp"),
|
||||
(
|
||||
self.image,
|
||||
["fill-400x400", "format-webp", "webpquality-50"],
|
||||
"fill-400x400|format-webp|webpquality-50",
|
||||
),
|
||||
(self.svg_image, ["max-400x400"], "max-400x400"),
|
||||
(self.svg_image, ["min-400x400"], "min-400x400"),
|
||||
(self.svg_image, ["width-300"], "width-300"),
|
||||
(self.svg_image, ["height-300"], "height-300"),
|
||||
(self.svg_image, ["scale-50"], "scale-50"),
|
||||
(self.svg_image, ["fill-400x400"], "fill-400x400"),
|
||||
]
|
||||
for image, filter_specs, expected in params:
|
||||
with self.subTest(img=image, filter_specs=filter_specs, expected=expected):
|
||||
context = {"image": image, "image_node": "fake_value"}
|
||||
node = ImageNode(Variable("image"), filter_specs, preserve_svg=True)
|
||||
node.render(context)
|
||||
self.assertEqual(
|
||||
node.get_filter(preserve_svg=image.is_svg()).spec, expected
|
||||
)
|
||||
|
||||
|
||||
class ImagesTestCase(TestCase):
|
||||
maxDiff = None
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.engine = Engine(
|
||||
app_dirs=True,
|
||||
libraries=LIBRARIES,
|
||||
builtins=[LIBRARIES["wagtailimages_tags"]],
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
# Create an image for running tests on
|
||||
cls.image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
cls.svg_image = Image.objects.create(
|
||||
title="Test SVG image",
|
||||
file=get_test_image_file_svg(),
|
||||
)
|
||||
cls.bad_image = get_test_bad_image()
|
||||
cls.bad_image.save()
|
||||
|
||||
def render(self, string, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
template = self.engine.from_string(string)
|
||||
return template.render(Context(context, autoescape=False))
|
||||
|
||||
|
||||
class ImageTagTestCase(ImagesTestCase):
|
||||
def test_image(self):
|
||||
filename_200 = get_test_image_filename(self.image, "width-200")
|
||||
|
||||
rendered = self.render("{% image myimage width-200 %}", {"myimage": self.image})
|
||||
self.assertHTMLEqual(
|
||||
rendered,
|
||||
f'<img alt="Test image" height="150" src="{filename_200}" width="200" />',
|
||||
)
|
||||
|
||||
def test_none(self):
|
||||
rendered = self.render("{% image myimage width-2 %}", {"myimage": None})
|
||||
self.assertEqual(rendered, "")
|
||||
|
||||
def test_missing_image(self):
|
||||
rendered = self.render(
|
||||
"{% image myimage width-200 %}", {"myimage": self.bad_image}
|
||||
)
|
||||
self.assertHTMLEqual(
|
||||
rendered,
|
||||
'<img alt="missing image" src="/media/not-found" width="0" height="0">',
|
||||
)
|
||||
|
||||
def test_not_an_image(self):
|
||||
with self.assertRaisesMessage(
|
||||
ValueError, "Image template tags expect an Image object, got 'not a pipe'"
|
||||
):
|
||||
self.render(
|
||||
"{% image myimage width-200 %}",
|
||||
{"myimage": "not a pipe"},
|
||||
)
|
||||
|
||||
def test_invalid_character(self):
|
||||
with self.assertRaisesRegex(
|
||||
TemplateSyntaxError, "filter specs in image tags may only"
|
||||
):
|
||||
self.render(
|
||||
"{% image myimage fill-200×200 %}",
|
||||
{"myimage": self.image},
|
||||
)
|
||||
|
||||
def test_multiple_as_variable(self):
|
||||
with self.assertRaisesRegex(
|
||||
TemplateSyntaxError, "More than one variable name after 'as'"
|
||||
):
|
||||
self.render(
|
||||
"{% image myimage width-200 as a b %}",
|
||||
{"myimage": self.image},
|
||||
)
|
||||
|
||||
def test_missing_as_variable(self):
|
||||
with self.assertRaisesRegex(
|
||||
TemplateSyntaxError, "Missing a variable name after 'as'"
|
||||
):
|
||||
self.render(
|
||||
"{% image myimage width-200 as %}",
|
||||
{"myimage": self.image},
|
||||
)
|
||||
|
||||
def test_mixing_as_variable_and_attrs(self):
|
||||
with self.assertRaisesRegex(
|
||||
TemplateSyntaxError, "Do not use attributes with 'as' context assignments"
|
||||
):
|
||||
self.render(
|
||||
"{% image myimage width-200 alt='Test' as test %}",
|
||||
{"myimage": self.image},
|
||||
)
|
||||
|
||||
def test_missing_filter_spec(self):
|
||||
with self.assertRaisesRegex(
|
||||
TemplateSyntaxError, "Image tags must be used with at least one filter spec"
|
||||
):
|
||||
self.render(
|
||||
"{% image myimage %}",
|
||||
{"myimage": self.image},
|
||||
)
|
||||
|
||||
|
||||
class SrcsetImageTagTestCase(ImagesTestCase):
|
||||
def test_srcset_image(self):
|
||||
filename_20 = get_test_image_filename(self.image, "width-20")
|
||||
filename_40 = get_test_image_filename(self.image, "width-40")
|
||||
|
||||
rendered = self.render(
|
||||
"{% srcset_image myimage width-{20,40} sizes='100vw' %}",
|
||||
{"myimage": self.image},
|
||||
)
|
||||
expected = f"""
|
||||
<img
|
||||
sizes="100vw"
|
||||
src="{filename_20}"
|
||||
srcset="{filename_20} 20w, {filename_40} 40w"
|
||||
alt="Test image"
|
||||
width="20"
|
||||
height="15"
|
||||
>
|
||||
"""
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_srcset_output_single_image(self):
|
||||
self.assertHTMLEqual(
|
||||
self.render(
|
||||
"{% srcset_image myimage width-20 %}",
|
||||
{"myimage": self.image},
|
||||
),
|
||||
self.render(
|
||||
"{% image myimage width-20 %}",
|
||||
{"myimage": self.image},
|
||||
),
|
||||
)
|
||||
|
||||
def test_none(self):
|
||||
rendered = self.render("{% srcset_image myimage width-2 %}", {"myimage": None})
|
||||
self.assertEqual(rendered, "")
|
||||
|
||||
def test_invalid_character(self):
|
||||
with self.assertRaisesRegex(
|
||||
TemplateSyntaxError, "filter specs in image tags may only contain"
|
||||
):
|
||||
self.render(
|
||||
"{% srcset_image myimage fill-{200×200,400×400} sizes='100vw' %}",
|
||||
{"myimage": self.image},
|
||||
)
|
||||
|
||||
def test_srcset_image_assignment(self):
|
||||
template = (
|
||||
"{% srcset_image myimage width-{30,60} as bg %}"
|
||||
"width: {{ bg.renditions.0.width }}, url: {{ bg.renditions.0.url }} "
|
||||
"width: {{ bg.renditions.1.width }}, url: {{ bg.renditions.1.url }} "
|
||||
)
|
||||
rendered = self.render(template, {"myimage": self.image})
|
||||
expected = f"""
|
||||
width: 30, url: {get_test_image_filename(self.image, "width-30")}
|
||||
width: 60, url: {get_test_image_filename(self.image, "width-60")}
|
||||
"""
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_srcset_image_assignment_render_as_is(self):
|
||||
filename_35 = get_test_image_filename(self.image, "width-35")
|
||||
filename_70 = get_test_image_filename(self.image, "width-70")
|
||||
|
||||
rendered = self.render(
|
||||
"{% srcset_image myimage width-{35,70} as bg %}{{ bg }}",
|
||||
{"myimage": self.image},
|
||||
)
|
||||
expected = f"""
|
||||
<img
|
||||
src="{filename_35}"
|
||||
srcset="{filename_35} 35w, {filename_70} 70w"
|
||||
alt="Test image"
|
||||
width="35"
|
||||
height="26"
|
||||
>
|
||||
"""
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_missing_srcset_image(self):
|
||||
rendered = self.render(
|
||||
"{% srcset_image myimage width-{200,400} sizes='100vw' %}",
|
||||
{"myimage": self.bad_image},
|
||||
)
|
||||
expected = """
|
||||
<img
|
||||
sizes="100vw"
|
||||
src="/media/not-found"
|
||||
srcset="/media/not-found 0w, /media/not-found 0w"
|
||||
alt="missing image"
|
||||
width="0"
|
||||
height="0"
|
||||
>
|
||||
"""
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
|
||||
class PictureTagTestCase(ImagesTestCase):
|
||||
def test_picture_formats_multi_sizes(self):
|
||||
filenames = [
|
||||
get_test_image_filename(self.image, "width-200.format-jpeg"),
|
||||
get_test_image_filename(self.image, "width-400.format-jpeg"),
|
||||
get_test_image_filename(self.image, "width-200.format-webp"),
|
||||
get_test_image_filename(self.image, "width-400.format-webp"),
|
||||
get_test_image_filename(self.image, "width-200.format-gif"),
|
||||
get_test_image_filename(self.image, "width-400.format-gif"),
|
||||
]
|
||||
|
||||
rendered = self.render(
|
||||
'{% picture myimage width-{200,400} format-{jpeg,webp,gif} sizes="100vw" %}',
|
||||
{"myimage": self.image},
|
||||
)
|
||||
expected = f"""
|
||||
<picture>
|
||||
<source srcset="{filenames[2]} 200w, {filenames[3]} 400w" sizes="100vw" type="image/webp">
|
||||
<source srcset="{filenames[0]} 200w, {filenames[1]} 400w" sizes="100vw" type="image/jpeg">
|
||||
<img
|
||||
sizes="100vw"
|
||||
src="{filenames[4]}"
|
||||
srcset="{filenames[4]} 200w, {filenames[5]} 400w"
|
||||
alt="Test image"
|
||||
width="200"
|
||||
height="150"
|
||||
>
|
||||
</picture>
|
||||
"""
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_picture_formats_only(self):
|
||||
filename_jpeg = get_test_image_filename(self.image, "format-jpeg")
|
||||
filename_webp = get_test_image_filename(self.image, "format-webp")
|
||||
|
||||
rendered = self.render(
|
||||
"{% picture myimage format-{jpeg,webp} %}",
|
||||
{"myimage": self.image},
|
||||
)
|
||||
expected = f"""
|
||||
<picture>
|
||||
<source srcset="{filename_webp}" type="image/webp">
|
||||
<img
|
||||
src="{filename_jpeg}"
|
||||
alt="Test image"
|
||||
width="640"
|
||||
height="480"
|
||||
>
|
||||
</picture>
|
||||
"""
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_picture_sizes_only(self):
|
||||
rendered = self.render(
|
||||
'{% picture myimage width-{350,450} sizes="100vw" %}',
|
||||
{"myimage": self.image},
|
||||
)
|
||||
expected = self.render(
|
||||
'<picture>{% srcset_image myimage width-{350,450} sizes="100vw" %}</picture>',
|
||||
{"myimage": self.image},
|
||||
)
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_picture_single_format(self):
|
||||
rendered = self.render(
|
||||
"{% picture myimage format-jpeg %}",
|
||||
{"myimage": self.image},
|
||||
)
|
||||
expected = self.render(
|
||||
"<picture>{% image myimage format-jpeg %}</picture>",
|
||||
{"myimage": self.image},
|
||||
)
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_none(self):
|
||||
rendered = self.render("{% picture myimage width-2 %}", {"myimage": None})
|
||||
self.assertEqual(rendered, "")
|
||||
|
||||
def test_picture_assignment(self):
|
||||
template = (
|
||||
"{% picture myimage width-{550,600} format-{jpeg,webp} as bg %}"
|
||||
"width: {{ bg.formats.jpeg.0.width }}, url: {{ bg.formats.jpeg.0.url }} "
|
||||
"width: {{ bg.formats.jpeg.1.width }}, url: {{ bg.formats.jpeg.1.url }} "
|
||||
"width: {{ bg.formats.webp.0.width }}, url: {{ bg.formats.webp.0.url }} "
|
||||
"width: {{ bg.formats.webp.1.width }}, url: {{ bg.formats.webp.1.url }} "
|
||||
)
|
||||
rendered = self.render(template, {"myimage": self.image})
|
||||
expected = f"""
|
||||
width: 550, url: {get_test_image_filename(self.image, "width-550.format-jpeg")}
|
||||
width: 600, url: {get_test_image_filename(self.image, "width-600.format-jpeg")}
|
||||
width: 550, url: {get_test_image_filename(self.image, "width-550.format-webp")}
|
||||
width: 600, url: {get_test_image_filename(self.image, "width-600.format-webp")}
|
||||
"""
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_picture_assignment_render_as_is(self):
|
||||
rendered = self.render(
|
||||
"{% picture myimage width-{2000,4000} format-{jpeg,webp} as bg %}{{ bg }}",
|
||||
{"myimage": self.image},
|
||||
)
|
||||
expected = self.render(
|
||||
"{% picture myimage width-{2000,4000} format-{jpeg,webp} %}",
|
||||
{"myimage": self.image},
|
||||
)
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_missing_picture(self):
|
||||
rendered = self.render(
|
||||
"{% picture myimage width-{200,400} %}",
|
||||
{"myimage": self.bad_image},
|
||||
)
|
||||
expected = """
|
||||
<picture>
|
||||
<img
|
||||
src="/media/not-found"
|
||||
srcset="/media/not-found 0w, /media/not-found 0w"
|
||||
alt="missing image"
|
||||
width="0"
|
||||
height="0"
|
||||
>
|
||||
</picture>
|
||||
"""
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
|
||||
def test_invalid_character(self):
|
||||
with self.assertRaisesRegex(
|
||||
TemplateSyntaxError, "filter specs in image tags may only"
|
||||
):
|
||||
self.render(
|
||||
'{% picture myimage fill-{20×20,40×40} sizes="100vw" %}',
|
||||
{"myimage": self.image},
|
||||
)
|
||||
|
||||
def test_chaining_filterspecs(self):
|
||||
filename_jpeg = get_test_image_filename(
|
||||
self.image, "format-jpeg.jpegquality-40.webpquality-40"
|
||||
)
|
||||
filename_webp = get_test_image_filename(
|
||||
self.image, "format-webp.jpegquality-40.webpquality-40"
|
||||
)
|
||||
rendered = self.render(
|
||||
"{% picture myimage format-{jpeg,webp} jpegquality-40 webpquality-40 %}",
|
||||
{"myimage": self.image},
|
||||
)
|
||||
expected = f"""
|
||||
<picture>
|
||||
<source srcset="{filename_webp}" type="image/webp">
|
||||
<img
|
||||
src="{filename_jpeg}"
|
||||
alt="Test image"
|
||||
width="640"
|
||||
height="480"
|
||||
>
|
||||
</picture>
|
||||
"""
|
||||
self.assertHTMLEqual(rendered, expected)
|
||||
60
env/lib/python3.10/site-packages/wagtail/images/tests/test_transform.py
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
from unittest import TestCase
|
||||
|
||||
from wagtail.images.image_operations import ImageTransform
|
||||
from wagtail.images.rect import Rect, Vector
|
||||
|
||||
|
||||
class TestTransform(TestCase):
|
||||
def test_resize(self):
|
||||
context = ImageTransform((640, 480))
|
||||
resized = context.resize((320, 240))
|
||||
|
||||
vector = Vector(100, 200)
|
||||
transformed = resized.transform_vector(vector)
|
||||
|
||||
self.assertEqual(transformed, Vector(50, 100))
|
||||
|
||||
untransformed = resized.untransform_vector(transformed)
|
||||
|
||||
self.assertEqual(untransformed, vector)
|
||||
|
||||
def test_crop(self):
|
||||
context = ImageTransform((640, 480))
|
||||
cropped = context.crop(Rect(200, 100, 300, 200))
|
||||
|
||||
vector = Vector(250, 150)
|
||||
transformed = cropped.transform_vector(vector)
|
||||
|
||||
self.assertEqual(transformed, Vector(50, 50))
|
||||
|
||||
untransformed = cropped.untransform_vector(transformed)
|
||||
|
||||
self.assertEqual(untransformed, vector)
|
||||
|
||||
def test_resize_then_crop(self):
|
||||
context = ImageTransform((640, 480))
|
||||
resized = context.resize((320, 240))
|
||||
cropped = resized.crop(Rect(200, 100, 300, 200))
|
||||
|
||||
vector = Vector(500, 300)
|
||||
transformed = cropped.transform_vector(vector)
|
||||
|
||||
self.assertEqual(transformed, Vector(50, 50))
|
||||
|
||||
untransformed = cropped.untransform_vector(transformed)
|
||||
|
||||
self.assertEqual(untransformed, vector)
|
||||
|
||||
def test_crop_then_resize(self):
|
||||
context = ImageTransform((640, 480))
|
||||
cropped = context.crop(Rect(200, 100, 300, 200))
|
||||
resized = cropped.resize((50, 50))
|
||||
|
||||
vector = Vector(250, 150)
|
||||
transformed = resized.transform_vector(vector)
|
||||
|
||||
self.assertEqual(transformed, Vector(25, 25))
|
||||
|
||||
untransformed = resized.untransform_vector(transformed)
|
||||
|
||||
self.assertEqual(untransformed, vector)
|
||||
958
env/lib/python3.10/site-packages/wagtail/images/tests/tests.py
vendored
Normal file
@@ -0,0 +1,958 @@
|
||||
import os
|
||||
import unittest
|
||||
from io import BytesIO
|
||||
|
||||
import willow
|
||||
from django import forms, template
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured, ValidationError
|
||||
from django.core.files.uploadedfile import InMemoryUploadedFile, TemporaryUploadedFile
|
||||
from django.test import TestCase, override_settings
|
||||
from django.test.signals import setting_changed
|
||||
from django.urls import reverse
|
||||
from taggit.forms import TagField, TagWidget
|
||||
from willow.image import (
|
||||
AvifImageFile,
|
||||
PNGImageFile,
|
||||
SvgImageFile,
|
||||
)
|
||||
from willow.image import ImageFile as WillowImageFile
|
||||
|
||||
from wagtail.images import get_image_model, get_image_model_string
|
||||
from wagtail.images.fields import WagtailImageField
|
||||
from wagtail.images.formats import Format, get_image_format, register_image_format
|
||||
from wagtail.images.forms import get_image_form
|
||||
from wagtail.images.models import Image as WagtailImage
|
||||
from wagtail.images.permissions import update_permission_policy
|
||||
from wagtail.images.rect import Rect, Vector
|
||||
from wagtail.images.utils import generate_signature, verify_signature
|
||||
from wagtail.images.views.serve import ServeView
|
||||
from wagtail.test.testapp.models import CustomImage, CustomImageFilePath
|
||||
from wagtail.test.utils import WagtailTestUtils, disconnect_signal_receiver
|
||||
from wagtail.utils.deprecation import RemovedInWagtail70Warning
|
||||
|
||||
from .utils import (
|
||||
Image,
|
||||
get_test_image_file,
|
||||
get_test_image_file_avif,
|
||||
get_test_image_file_svg,
|
||||
)
|
||||
|
||||
try:
|
||||
import sendfile # noqa: F401
|
||||
|
||||
sendfile_mod = True
|
||||
except ImportError:
|
||||
sendfile_mod = False
|
||||
|
||||
|
||||
class TestImageTag(TestCase):
|
||||
def setUp(self):
|
||||
# Create an image for running tests on
|
||||
self.image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
def render_image_tag(self, image, filter_spec):
|
||||
temp = template.Template(
|
||||
"{% load wagtailimages_tags %}{% image image_obj " + filter_spec + "%}"
|
||||
)
|
||||
context = template.Context({"image_obj": image})
|
||||
return temp.render(context)
|
||||
|
||||
def test_image_tag(self):
|
||||
result = self.render_image_tag(self.image, "width-400")
|
||||
|
||||
# Check that all the required HTML attributes are set
|
||||
self.assertIn('width="400"', result)
|
||||
self.assertIn('height="300"', result)
|
||||
self.assertIn('alt="Test image"', result)
|
||||
|
||||
def test_image_tag_none(self):
|
||||
result = self.render_image_tag(None, "width-500")
|
||||
self.assertEqual(result, "")
|
||||
|
||||
def test_image_tag_wrong_type(self):
|
||||
with self.assertRaises(ValueError):
|
||||
self.render_image_tag("foobar", "width-500")
|
||||
|
||||
def render_image_tag_as(self, image, filter_spec):
|
||||
temp = template.Template(
|
||||
"{% load wagtailimages_tags %}{% image image_obj "
|
||||
+ filter_spec
|
||||
+ " as test_img %}<img {{ test_img.attrs }} />"
|
||||
)
|
||||
context = template.Context({"image_obj": image})
|
||||
return temp.render(context)
|
||||
|
||||
def test_image_tag_attrs(self):
|
||||
result = self.render_image_tag_as(self.image, "width-400")
|
||||
|
||||
# Check that all the required HTML attributes are set
|
||||
self.assertIn('width="400"', result)
|
||||
self.assertIn('height="300"', result)
|
||||
self.assertIn('alt="Test image"', result)
|
||||
|
||||
def render_image_tag_with_extra_attributes(self, image, title):
|
||||
temp = template.Template(
|
||||
'{% load wagtailimages_tags %}{% image image_obj width-400 \
|
||||
class="photo" title=title|lower alt="Alternate" %}'
|
||||
)
|
||||
context = template.Context({"image_obj": image, "title": title})
|
||||
return temp.render(context)
|
||||
|
||||
def test_image_tag_with_extra_attributes(self):
|
||||
result = self.render_image_tag_with_extra_attributes(
|
||||
self.image, "My Wonderful Title"
|
||||
)
|
||||
|
||||
# Check that all the required HTML attributes are set
|
||||
self.assertIn('width="400"', result)
|
||||
self.assertIn('height="300"', result)
|
||||
self.assertIn('class="photo"', result)
|
||||
self.assertIn('alt="Alternate"', result)
|
||||
self.assertIn('title="my wonderful title"', result)
|
||||
|
||||
def render_image_tag_with_filters(self, image):
|
||||
temp = template.Template(
|
||||
"{% load wagtailimages_tags %}{% image image_primary|default:image_alternate width-400 %}"
|
||||
)
|
||||
context = template.Context({"image_primary": None, "image_alternate": image})
|
||||
return temp.render(context)
|
||||
|
||||
def test_image_tag_with_filters(self):
|
||||
result = self.render_image_tag_with_filters(self.image)
|
||||
self.assertIn('width="400"', result)
|
||||
self.assertIn('height="300"', result)
|
||||
|
||||
def test_image_tag_with_chained_filters(self):
|
||||
result = self.render_image_tag(self.image, "fill-200x200 height-150")
|
||||
self.assertIn('width="150"', result)
|
||||
self.assertIn('height="150"', result)
|
||||
|
||||
def test_filter_specs_must_match_allowed_pattern(self):
|
||||
with self.assertRaises(template.TemplateSyntaxError):
|
||||
self.render_image_tag(self.image, "fill-200x200|height-150")
|
||||
|
||||
with self.assertRaises(template.TemplateSyntaxError):
|
||||
self.render_image_tag(self.image, 'fill-800x600 alt"test"')
|
||||
|
||||
def test_context_may_only_contain_one_argument(self):
|
||||
with self.assertRaises(template.TemplateSyntaxError):
|
||||
temp = template.Template(
|
||||
"{% load wagtailimages_tags %}{% image image_obj fill-200x200"
|
||||
" as test_img this_one_should_not_be_there %}<img {{ test_img.attrs }} />"
|
||||
)
|
||||
context = template.Context({"image_obj": self.image})
|
||||
temp.render(context)
|
||||
|
||||
def test_no_image_filter_provided(self):
|
||||
# if image template gets the image but no filters
|
||||
with self.assertRaises(template.TemplateSyntaxError):
|
||||
temp = template.Template(
|
||||
"{% load wagtailimages_tags %}{% image image_obj %}"
|
||||
)
|
||||
context = template.Context({"image_obj": self.image})
|
||||
temp.render(context)
|
||||
|
||||
def test_no_image_filter_provided_when_using_as(self):
|
||||
# if image template gets the image but no filters
|
||||
with self.assertRaises(template.TemplateSyntaxError):
|
||||
temp = template.Template(
|
||||
"{% load wagtailimages_tags %}{% image image_obj as foo %}"
|
||||
)
|
||||
context = template.Context({"image_obj": self.image})
|
||||
temp.render(context)
|
||||
|
||||
def test_no_image_filter_provided_but_attributes_provided(self):
|
||||
# if image template gets the image but no filters
|
||||
with self.assertRaises(template.TemplateSyntaxError):
|
||||
temp = template.Template(
|
||||
'{% load wagtailimages_tags %}{% image image_obj class="cover-image"%}'
|
||||
)
|
||||
context = template.Context({"image_obj": self.image})
|
||||
temp.render(context)
|
||||
|
||||
def render_image_url_tag(self, image, view_name):
|
||||
temp = template.Template(
|
||||
'{% load wagtailimages_tags %}{% image_url image_obj "width-400" "'
|
||||
+ view_name
|
||||
+ '" %}'
|
||||
)
|
||||
context = template.Context({"image_obj": image})
|
||||
return temp.render(context)
|
||||
|
||||
def test_image_url(self):
|
||||
result = self.render_image_url_tag(self.image, "wagtailimages_serve")
|
||||
self.assertRegex(
|
||||
result,
|
||||
"/images/.*/width-400/{}".format(self.image.file.name.split("/")[-1]),
|
||||
)
|
||||
|
||||
def test_image_url_custom_view(self):
|
||||
result = self.render_image_url_tag(
|
||||
self.image, "wagtailimages_serve_custom_view"
|
||||
)
|
||||
|
||||
self.assertRegex(
|
||||
result,
|
||||
"/testimages/custom_view/.*/width-400/{}".format(
|
||||
self.image.file.name.split("/")[-1]
|
||||
),
|
||||
)
|
||||
|
||||
def test_image_url_no_imageserve_view_added(self):
|
||||
# if image_url tag is used, but the image serve view was not defined.
|
||||
with self.assertRaises(ImproperlyConfigured):
|
||||
temp = template.Template(
|
||||
'{% load wagtailimages_tags %}{% image_url image_obj "width-400" "mynonexistingimageserve_view" %}'
|
||||
)
|
||||
context = template.Context({"image_obj": self.image})
|
||||
temp.render(context)
|
||||
|
||||
|
||||
class TestMissingImage(TestCase):
|
||||
"""
|
||||
Missing image files in media/original_images should be handled gracefully, to cope with
|
||||
pulling live databases to a development instance without copying the corresponding image files.
|
||||
In this case, it's acceptable to render broken images, but not to fail rendering the page outright.
|
||||
"""
|
||||
|
||||
fixtures = ["test.json"]
|
||||
|
||||
def test_image_tag_with_missing_image(self):
|
||||
# the page /events/christmas/ has a missing image as the feed image
|
||||
response = self.client.get("/events/christmas/")
|
||||
self.assertContains(
|
||||
response,
|
||||
'<img src="/media/not-found" width="0" height="0" alt="A missing image" \
|
||||
class="feed-image">',
|
||||
html=True,
|
||||
)
|
||||
|
||||
def test_rich_text_with_missing_image(self):
|
||||
# the page /events/final-event/ has a missing image in the rich text body
|
||||
response = self.client.get("/events/final-event/")
|
||||
self.assertContains(
|
||||
response,
|
||||
'<img class="richtext-image full-width" src="/media/not-found" \
|
||||
width="0" height="0" alt="where did my image go?">',
|
||||
html=True,
|
||||
)
|
||||
|
||||
|
||||
class TestFormat(WagtailTestUtils, TestCase):
|
||||
def setUp(self):
|
||||
# test format
|
||||
self.format = Format("test name", "test label", "test is-primary", "original")
|
||||
# test image
|
||||
self.image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
def test_editor_attributes(self):
|
||||
result = self.format.editor_attributes(self.image, "test alt text")
|
||||
self.assertEqual(
|
||||
result,
|
||||
{
|
||||
"data-alt": "test alt text",
|
||||
"data-embedtype": "image",
|
||||
"data-format": "test name",
|
||||
"data-id": self.image.pk,
|
||||
},
|
||||
)
|
||||
|
||||
def test_image_to_editor_html(self):
|
||||
result = self.format.image_to_editor_html(self.image, "test alt text")
|
||||
self.assertTagInHTML(
|
||||
'<img data-embedtype="image" data-id="%d" data-format="test name" '
|
||||
'data-alt="test alt text" class="test is-primary" '
|
||||
'width="640" height="480" alt="test alt text" >' % self.image.pk,
|
||||
result,
|
||||
allow_extra_attrs=True,
|
||||
)
|
||||
|
||||
def test_image_to_editor_html_with_quoting(self):
|
||||
result = self.format.image_to_editor_html(
|
||||
self.image, 'Arthur "two sheds" Jackson'
|
||||
)
|
||||
expected_html = (
|
||||
'<img data-embedtype="image" data-id="%d" data-format="test name" '
|
||||
'data-alt="Arthur "two sheds" Jackson" class="test is-primary" '
|
||||
'width="640" height="480" alt="Arthur "two sheds" Jackson" >'
|
||||
% self.image.pk
|
||||
)
|
||||
self.assertTagInHTML(expected_html, result, allow_extra_attrs=True)
|
||||
|
||||
def test_image_to_html_no_classnames(self):
|
||||
self.format.classname = None
|
||||
result = self.format.image_to_html(self.image, "test alt text")
|
||||
self.assertTagInHTML(
|
||||
'<img width="640" height="480" alt="test alt text">',
|
||||
result,
|
||||
allow_extra_attrs=True,
|
||||
)
|
||||
self.format.classname = (
|
||||
"test is-primary" # reset to original value for other tests
|
||||
)
|
||||
|
||||
def test_image_to_html_with_quoting(self):
|
||||
result = self.format.image_to_html(self.image, 'Arthur "two sheds" Jackson')
|
||||
self.assertTagInHTML(
|
||||
'<img class="test is-primary" width="640" height="480" '
|
||||
'alt="Arthur "two sheds" Jackson">',
|
||||
result,
|
||||
allow_extra_attrs=True,
|
||||
)
|
||||
|
||||
def test_get_image_format(self):
|
||||
register_image_format(self.format)
|
||||
result = get_image_format("test name")
|
||||
self.assertEqual(result, self.format)
|
||||
|
||||
def test_deprecated_classnames_property_access(self):
|
||||
with self.assertWarns(RemovedInWagtail70Warning):
|
||||
classname = self.format.classnames
|
||||
self.assertEqual(classname, "test is-primary")
|
||||
|
||||
|
||||
class TestSignatureGeneration(TestCase):
|
||||
def test_signature_generation(self):
|
||||
self.assertEqual(
|
||||
generate_signature(100, "fill-800x600"), "xnZOzQyUg6pkfciqcfRJRosOrGg="
|
||||
)
|
||||
|
||||
def test_signature_verification(self):
|
||||
self.assertTrue(
|
||||
verify_signature("xnZOzQyUg6pkfciqcfRJRosOrGg=", 100, "fill-800x600")
|
||||
)
|
||||
|
||||
def test_signature_changes_on_image_id(self):
|
||||
self.assertFalse(
|
||||
verify_signature("xnZOzQyUg6pkfciqcfRJRosOrGg=", 200, "fill-800x600")
|
||||
)
|
||||
|
||||
def test_signature_changes_on_filter_spec(self):
|
||||
self.assertFalse(
|
||||
verify_signature("xnZOzQyUg6pkfciqcfRJRosOrGg=", 100, "fill-800x700")
|
||||
)
|
||||
|
||||
|
||||
class TestFrontendServeView(TestCase):
|
||||
def setUp(self):
|
||||
# Create an image for running tests on
|
||||
self.image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
def test_get(self):
|
||||
"""
|
||||
Test a valid GET request to the view
|
||||
"""
|
||||
# Generate signature
|
||||
signature = generate_signature(self.image.id, "fill-800x600")
|
||||
|
||||
# Get the image
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wagtailimages_serve", args=(signature, self.image.id, "fill-800x600")
|
||||
)
|
||||
)
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(response.streaming)
|
||||
self.assertEqual(response["Content-Type"], "image/png")
|
||||
# Ensure the file can actually be read
|
||||
image = willow.Image.open(b"".join(response.streaming_content))
|
||||
self.assertIsInstance(image, PNGImageFile)
|
||||
|
||||
def test_get_svg(self):
|
||||
image = Image.objects.create(title="Test SVG", file=get_test_image_file_svg())
|
||||
|
||||
# Generate signature
|
||||
signature = generate_signature(image.id, "fill-800x600")
|
||||
|
||||
# Get the image
|
||||
response = self.client.get(
|
||||
reverse("wagtailimages_serve", args=(signature, image.id, "fill-800x600"))
|
||||
)
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(response.streaming)
|
||||
self.assertEqual(response["Content-Type"], "image/svg+xml")
|
||||
# Ensure the file can actually be read
|
||||
image = willow.Image.open(BytesIO(b"".join(response.streaming_content)))
|
||||
self.assertIsInstance(image, SvgImageFile)
|
||||
|
||||
@override_settings(WAGTAILIMAGES_FORMAT_CONVERSIONS={"avif": "avif"})
|
||||
def test_get_avif(self):
|
||||
image = Image.objects.create(title="Test AVIF", file=get_test_image_file_avif())
|
||||
|
||||
# Generate signature
|
||||
signature = generate_signature(image.id, "fill-800x600")
|
||||
|
||||
# Get the image
|
||||
response = self.client.get(
|
||||
reverse("wagtailimages_serve", args=(signature, image.id, "fill-800x600"))
|
||||
)
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(response.streaming)
|
||||
self.assertEqual(response["Content-Type"], "image/avif")
|
||||
# Ensure the file can actually be read
|
||||
image = willow.Image.open(b"".join(response.streaming_content))
|
||||
self.assertIsInstance(image, AvifImageFile)
|
||||
|
||||
def test_get_with_extra_component(self):
|
||||
"""
|
||||
Test that a filename can be optionally added to the end of the URL.
|
||||
"""
|
||||
# Generate signature
|
||||
signature = generate_signature(self.image.id, "fill-800x600")
|
||||
|
||||
# Get the image
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wagtailimages_serve", args=(signature, self.image.id, "fill-800x600")
|
||||
)
|
||||
+ "test.png"
|
||||
)
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(response.streaming)
|
||||
self.assertEqual(response["Content-Type"], "image/png")
|
||||
# Ensure the file can actually be read
|
||||
image = willow.Image.open(b"".join(response.streaming_content))
|
||||
self.assertIsInstance(image, PNGImageFile)
|
||||
|
||||
def test_get_with_too_many_extra_components(self):
|
||||
"""
|
||||
A filename can be appended to the end of the URL, but it must not contain a '/'
|
||||
"""
|
||||
# Generate signature
|
||||
signature = generate_signature(self.image.id, "fill-800x600")
|
||||
|
||||
# Get the image
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wagtailimages_serve", args=(signature, self.image.id, "fill-800x600")
|
||||
)
|
||||
+ "test/test.png"
|
||||
)
|
||||
|
||||
# URL pattern should not match
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_get_with_serve_action(self):
|
||||
signature = generate_signature(self.image.id, "fill-800x600")
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wagtailimages_serve_action_serve",
|
||||
args=(signature, self.image.id, "fill-800x600"),
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(response.streaming)
|
||||
self.assertEqual(response["Content-Type"], "image/png")
|
||||
# Ensure the file can actually be read
|
||||
image = willow.Image.open(b"".join(response.streaming_content))
|
||||
self.assertIsInstance(image, PNGImageFile)
|
||||
|
||||
def test_get_with_redirect_action(self):
|
||||
signature = generate_signature(self.image.id, "fill-800x600")
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wagtailimages_serve_action_redirect",
|
||||
args=(signature, self.image.id, "fill-800x600"),
|
||||
)
|
||||
)
|
||||
|
||||
expected_redirect_url = (
|
||||
"/media/images/{filename[0]}.2e16d0ba.fill-800x600{filename[1]}".format(
|
||||
filename=os.path.splitext(os.path.basename(self.image.file.path))
|
||||
)
|
||||
)
|
||||
|
||||
self.assertRedirects(
|
||||
response,
|
||||
expected_redirect_url,
|
||||
status_code=302,
|
||||
fetch_redirect_response=False,
|
||||
)
|
||||
|
||||
def test_init_with_unknown_action_raises_error(self):
|
||||
with self.assertRaises(ImproperlyConfigured):
|
||||
ServeView.as_view(action="unknown")
|
||||
|
||||
def test_get_with_custom_key(self):
|
||||
"""
|
||||
Test that the key can be changed on the view
|
||||
"""
|
||||
# Generate signature
|
||||
signature = generate_signature(self.image.id, "fill-800x600", key="custom")
|
||||
|
||||
# Get the image
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wagtailimages_serve_custom_key",
|
||||
args=(signature, self.image.id, "fill-800x600"),
|
||||
)
|
||||
+ "test.png"
|
||||
)
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
# Ensure the file can actually be read
|
||||
image = willow.Image.open(b"".join(response.streaming_content))
|
||||
self.assertIsInstance(image, PNGImageFile)
|
||||
|
||||
def test_get_with_custom_key_using_default_key(self):
|
||||
"""
|
||||
Test that the key can be changed on the view
|
||||
|
||||
This tests that the default key no longer works when the key is changed on the view
|
||||
"""
|
||||
# Generate signature
|
||||
signature = generate_signature(self.image.id, "fill-800x600")
|
||||
|
||||
# Get the image
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wagtailimages_serve_custom_key",
|
||||
args=(signature, self.image.id, "fill-800x600"),
|
||||
)
|
||||
+ "test.png"
|
||||
)
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
def test_get_invalid_signature(self):
|
||||
"""
|
||||
Test that an invalid signature returns a 403 response
|
||||
"""
|
||||
# Generate a signature for the incorrect image id
|
||||
signature = generate_signature(self.image.id + 1, "fill-800x600")
|
||||
|
||||
# Get the image
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wagtailimages_serve", args=(signature, self.image.id, "fill-800x600")
|
||||
)
|
||||
)
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
def test_get_invalid_filter_spec(self):
|
||||
"""
|
||||
Test that an invalid filter spec returns a 400 response
|
||||
|
||||
This is very unlikely to happen in reality. A user would have
|
||||
to create signature for the invalid filter spec which can't be
|
||||
done with Wagtails built in URL generator. We should test it
|
||||
anyway though.
|
||||
"""
|
||||
# Generate a signature with the invalid filterspec
|
||||
signature = generate_signature(self.image.id, "bad-filter-spec")
|
||||
|
||||
# Get the image
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wagtailimages_serve",
|
||||
args=(signature, self.image.id, "bad-filter-spec"),
|
||||
)
|
||||
)
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
def test_get_missing_source_image_file(self):
|
||||
"""
|
||||
Test that a missing image file gives a 410 response
|
||||
|
||||
When the source image file is missing, it is presumed deleted so we
|
||||
return a 410 "Gone" response.
|
||||
"""
|
||||
# Delete the image file
|
||||
os.remove(self.image.file.path)
|
||||
|
||||
# Get the image
|
||||
signature = generate_signature(self.image.id, "fill-800x600")
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wagtailimages_serve", args=(signature, self.image.id, "fill-800x600")
|
||||
)
|
||||
)
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 410)
|
||||
|
||||
def test_get_cache_control(self):
|
||||
signature = generate_signature(self.image.id, "fill-800x600")
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wagtailimages_serve_action_serve",
|
||||
args=(signature, self.image.id, "fill-800x600"),
|
||||
)
|
||||
)
|
||||
self.assertEqual(response["Cache-Control"], "max-age=3600, public")
|
||||
|
||||
|
||||
class TestFrontendSendfileView(TestCase):
|
||||
def setUp(self):
|
||||
self.image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
@override_settings(SENDFILE_BACKEND="sendfile.backends.development")
|
||||
@unittest.skipIf(not sendfile_mod, "Missing django-sendfile app.")
|
||||
def test_sendfile_nobackend(self):
|
||||
signature = generate_signature(self.image.id, "fill-800x600")
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wagtailimages_sendfile",
|
||||
args=(signature, self.image.id, "fill-800x600"),
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response["Content-Type"], "image/png")
|
||||
|
||||
@override_settings(SENDFILE_BACKEND="sendfile.backends.development")
|
||||
def test_sendfile_dummy_backend(self):
|
||||
signature = generate_signature(self.image.id, "fill-800x600")
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wagtailimages_sendfile_dummy",
|
||||
args=(signature, self.image.id, "fill-800x600"),
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(response.content, msg="Dummy backend response")
|
||||
|
||||
|
||||
class TestRect(TestCase):
|
||||
def test_init(self):
|
||||
rect = Rect(100, 150, 200, 250)
|
||||
self.assertEqual(rect.left, 100)
|
||||
self.assertEqual(rect.top, 150)
|
||||
self.assertEqual(rect.right, 200)
|
||||
self.assertEqual(rect.bottom, 250)
|
||||
|
||||
def test_equality(self):
|
||||
self.assertEqual(Rect(100, 150, 200, 250), Rect(100, 150, 200, 250))
|
||||
self.assertNotEqual(Rect(100, 150, 200, 250), Rect(10, 15, 20, 25))
|
||||
|
||||
def test_getitem(self):
|
||||
rect = Rect(100, 150, 200, 250)
|
||||
self.assertEqual(rect[0], 100)
|
||||
self.assertEqual(rect[1], 150)
|
||||
self.assertEqual(rect[2], 200)
|
||||
self.assertEqual(rect[3], 250)
|
||||
self.assertRaises(IndexError, rect.__getitem__, 4)
|
||||
|
||||
def test_as_tuple(self):
|
||||
rect = Rect(100, 150, 200, 250)
|
||||
self.assertEqual(rect.as_tuple(), (100, 150, 200, 250))
|
||||
|
||||
def test_size(self):
|
||||
rect = Rect(100, 150, 200, 350)
|
||||
self.assertIsInstance(rect.size, Vector)
|
||||
self.assertEqual(rect.size, (100, 200))
|
||||
self.assertEqual(rect.width, 100)
|
||||
self.assertEqual(rect.height, 200)
|
||||
|
||||
def test_set_size_with_tuple(self):
|
||||
rect = Rect(100, 150, 200, 350)
|
||||
rect.size = (200, 400)
|
||||
self.assertEqual(rect, (50, 50, 250, 450))
|
||||
|
||||
def test_set_size_with_vector(self):
|
||||
rect = Rect(100, 150, 200, 350)
|
||||
rect.size = Vector(200, 400)
|
||||
self.assertEqual(rect, (50, 50, 250, 450))
|
||||
|
||||
def test_centroid(self):
|
||||
rect = Rect(100, 150, 200, 350)
|
||||
self.assertIsInstance(rect.centroid, Vector)
|
||||
self.assertEqual(rect.centroid, (150, 250))
|
||||
self.assertEqual(rect.x, 150)
|
||||
self.assertEqual(rect.y, 250)
|
||||
self.assertEqual(rect.centroid_x, 150)
|
||||
self.assertEqual(rect.centroid_y, 250)
|
||||
|
||||
def test_set_centroid_with_tuple(self):
|
||||
rect = Rect(100, 150, 200, 350)
|
||||
rect.centroid = (500, 500)
|
||||
self.assertEqual(rect, (450, 400, 550, 600))
|
||||
|
||||
def test_set_centroid_with_vector(self):
|
||||
rect = Rect(100, 150, 200, 350)
|
||||
rect.centroid = Vector(500, 500)
|
||||
self.assertEqual(rect, (450, 400, 550, 600))
|
||||
|
||||
def test_repr(self):
|
||||
rect = Rect(100, 150, 200, 250)
|
||||
self.assertEqual(
|
||||
repr(rect), "Rect(left: 100, top: 150, right: 200, bottom: 250)"
|
||||
)
|
||||
|
||||
def test_from_point(self):
|
||||
rect = Rect.from_point(100, 200, 50, 20)
|
||||
self.assertEqual(rect, Rect(75, 190, 125, 210))
|
||||
|
||||
|
||||
class TestGetImageForm(WagtailTestUtils, TestCase):
|
||||
def test_fields(self):
|
||||
form = get_image_form(Image)
|
||||
|
||||
self.assertEqual(
|
||||
list(form.base_fields.keys()),
|
||||
[
|
||||
"title",
|
||||
"file",
|
||||
"collection",
|
||||
"tags",
|
||||
"focal_point_x",
|
||||
"focal_point_y",
|
||||
"focal_point_width",
|
||||
"focal_point_height",
|
||||
],
|
||||
)
|
||||
|
||||
def test_admin_form_fields_attribute(self):
|
||||
form = get_image_form(CustomImage)
|
||||
|
||||
self.assertEqual(
|
||||
list(form.base_fields.keys()),
|
||||
[
|
||||
"title",
|
||||
"file",
|
||||
"collection",
|
||||
"tags",
|
||||
"focal_point_x",
|
||||
"focal_point_y",
|
||||
"focal_point_width",
|
||||
"focal_point_height",
|
||||
"caption",
|
||||
"fancy_caption",
|
||||
],
|
||||
)
|
||||
|
||||
def test_file_field(self):
|
||||
form = get_image_form(WagtailImage)
|
||||
|
||||
self.assertIsInstance(form.base_fields["file"], WagtailImageField)
|
||||
self.assertIsInstance(form.base_fields["file"].widget, forms.FileInput)
|
||||
|
||||
def test_tags_field(self):
|
||||
form = get_image_form(WagtailImage)
|
||||
|
||||
self.assertIsInstance(form.base_fields["tags"], TagField)
|
||||
self.assertIsInstance(form.base_fields["tags"].widget, TagWidget)
|
||||
|
||||
def test_focal_point_fields(self):
|
||||
form = get_image_form(WagtailImage)
|
||||
|
||||
self.assertIsInstance(form.base_fields["focal_point_x"], forms.IntegerField)
|
||||
self.assertIsInstance(form.base_fields["focal_point_y"], forms.IntegerField)
|
||||
self.assertIsInstance(form.base_fields["focal_point_width"], forms.IntegerField)
|
||||
self.assertIsInstance(
|
||||
form.base_fields["focal_point_height"], forms.IntegerField
|
||||
)
|
||||
|
||||
self.assertIsInstance(
|
||||
form.base_fields["focal_point_x"].widget, forms.HiddenInput
|
||||
)
|
||||
self.assertIsInstance(
|
||||
form.base_fields["focal_point_y"].widget, forms.HiddenInput
|
||||
)
|
||||
self.assertIsInstance(
|
||||
form.base_fields["focal_point_width"].widget, forms.HiddenInput
|
||||
)
|
||||
self.assertIsInstance(
|
||||
form.base_fields["focal_point_height"].widget, forms.HiddenInput
|
||||
)
|
||||
|
||||
|
||||
class TestRenditionFilenames(TestCase):
|
||||
# Can't create image in setUp as we need a unique filename for each test.
|
||||
# This stops Django appending some rubbish to the filename which makes
|
||||
# the assertions difficult.
|
||||
|
||||
def test_normal_filter(self):
|
||||
image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(filename="test_rf1.png"),
|
||||
)
|
||||
rendition = image.get_rendition("width-100")
|
||||
|
||||
self.assertEqual(rendition.file.name, "images/test_rf1.width-100.png")
|
||||
|
||||
def test_fill_filter(self):
|
||||
image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(filename="test_rf2.png"),
|
||||
)
|
||||
rendition = image.get_rendition("fill-100x100")
|
||||
|
||||
self.assertEqual(
|
||||
rendition.file.name, "images/test_rf2.2e16d0ba.fill-100x100.png"
|
||||
)
|
||||
|
||||
def test_fill_filter_with_focal_point(self):
|
||||
image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(filename="test_rf3.png"),
|
||||
)
|
||||
image.set_focal_point(Rect(100, 100, 200, 200))
|
||||
image.save()
|
||||
|
||||
rendition = image.get_rendition("fill-100x100")
|
||||
|
||||
self.assertEqual(
|
||||
rendition.file.name, "images/test_rf3.15ee4958.fill-100x100.png"
|
||||
)
|
||||
|
||||
def test_filter_with_pipe_gets_dotted(self):
|
||||
image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(filename="test_rf4.png"),
|
||||
)
|
||||
image.set_focal_point(Rect(100, 100, 200, 200))
|
||||
image.save()
|
||||
|
||||
rendition = image.get_rendition("fill-200x200|height-150")
|
||||
|
||||
self.assertEqual(
|
||||
rendition.file.name, "images/test_rf4.15ee4958.fill-200x200.height-150.png"
|
||||
)
|
||||
|
||||
|
||||
class TestDifferentUpload(TestCase):
|
||||
def test_upload_path(self):
|
||||
image = CustomImageFilePath.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
second_image = CustomImageFilePath.objects.create(
|
||||
title="Test Image",
|
||||
file=get_test_image_file(colour="black"),
|
||||
)
|
||||
|
||||
# The files should be uploaded based on it's content, not just
|
||||
# it's filename
|
||||
self.assertNotEqual(image.file.url, second_image.file.url)
|
||||
|
||||
|
||||
class TestGetImageModel(WagtailTestUtils, TestCase):
|
||||
@override_settings(WAGTAILIMAGES_IMAGE_MODEL="tests.CustomImage")
|
||||
def test_custom_get_image_model(self):
|
||||
"""Test get_image_model with a custom image model"""
|
||||
self.assertIs(get_image_model(), CustomImage)
|
||||
|
||||
@override_settings(WAGTAILIMAGES_IMAGE_MODEL="tests.CustomImage")
|
||||
def test_custom_get_image_model_string(self):
|
||||
"""Test get_image_model_string with a custom image model"""
|
||||
self.assertEqual(get_image_model_string(), "tests.CustomImage")
|
||||
|
||||
@override_settings()
|
||||
def test_standard_get_image_model(self):
|
||||
"""Test get_image_model with no WAGTAILIMAGES_IMAGE_MODEL"""
|
||||
del settings.WAGTAILIMAGES_IMAGE_MODEL
|
||||
from wagtail.images.models import Image
|
||||
|
||||
self.assertIs(get_image_model(), Image)
|
||||
|
||||
@override_settings()
|
||||
def test_standard_get_image_model_string(self):
|
||||
"""Test get_image_model_STRING with no WAGTAILIMAGES_IMAGE_MODEL"""
|
||||
del settings.WAGTAILIMAGES_IMAGE_MODEL
|
||||
self.assertEqual(get_image_model_string(), "wagtailimages.Image")
|
||||
|
||||
@disconnect_signal_receiver(
|
||||
signal=setting_changed, receiver=update_permission_policy
|
||||
)
|
||||
@override_settings(WAGTAILIMAGES_IMAGE_MODEL="tests.UnknownModel")
|
||||
def test_unknown_get_image_model(self):
|
||||
"""Test get_image_model with an unknown model"""
|
||||
with self.assertRaises(ImproperlyConfigured):
|
||||
get_image_model()
|
||||
|
||||
@disconnect_signal_receiver(
|
||||
signal=setting_changed, receiver=update_permission_policy
|
||||
)
|
||||
@override_settings(WAGTAILIMAGES_IMAGE_MODEL="invalid-string")
|
||||
def test_invalid_get_image_model(self):
|
||||
"""Test get_image_model with an invalid model string"""
|
||||
with self.assertRaises(ImproperlyConfigured):
|
||||
get_image_model()
|
||||
|
||||
|
||||
class TestWagtailImageField(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.filename = "wagtailimagefield.png"
|
||||
cls.image = get_test_image_file(filename=cls.filename).file
|
||||
cls.image_size = cls.image.getbuffer().nbytes
|
||||
|
||||
def test_to_python_with_inmemoryfile(self):
|
||||
f = WagtailImageField()
|
||||
self.image.seek(0)
|
||||
file = InMemoryUploadedFile(
|
||||
self.image, "", self.filename, "image/png", self.image_size, None
|
||||
)
|
||||
to_python = f.to_python(file)
|
||||
self.assertIsInstance(to_python.image, WillowImageFile)
|
||||
self.assertEqual(to_python.content_type, "image/png")
|
||||
|
||||
def test_to_python_gets_content_type_from_willow(self):
|
||||
f = WagtailImageField()
|
||||
self.image.seek(0)
|
||||
file = InMemoryUploadedFile(
|
||||
self.image, "", self.filename, "image/jpeg", self.image_size, None
|
||||
)
|
||||
to_python = f.to_python(file)
|
||||
self.assertIsInstance(to_python.image, WillowImageFile)
|
||||
self.assertEqual(to_python.content_type, "image/png")
|
||||
|
||||
def test_to_python_with_temporary_file(self):
|
||||
f = WagtailImageField()
|
||||
with TemporaryUploadedFile(
|
||||
"test_temp.png", "image/png", self.image_size, None
|
||||
) as tmp_file:
|
||||
self.image.seek(0)
|
||||
tmp_file.write(self.image.read())
|
||||
tmp_file.seek(0)
|
||||
|
||||
to_python = f.to_python(tmp_file)
|
||||
self.assertIsInstance(to_python.image, WillowImageFile)
|
||||
self.assertEqual(to_python.content_type, "image/png")
|
||||
|
||||
def test_to_python_raises_error_with_invalid_image_file(self):
|
||||
msg = (
|
||||
"Upload a valid image. The file you uploaded was either not an "
|
||||
"image or a corrupted image."
|
||||
)
|
||||
f = WagtailImageField()
|
||||
with TemporaryUploadedFile("test_temp.png", "image/png", 32, None) as tmp_file:
|
||||
with self.assertRaisesMessage(ValidationError, msg):
|
||||
f.to_python(tmp_file)
|
||||
|
||||
with self.assertRaisesMessage(
|
||||
ValidationError,
|
||||
"No file was submitted. Check the encoding type on the form.",
|
||||
):
|
||||
f.to_python(self.image)
|
||||
38
env/lib/python3.10/site-packages/wagtail/images/tests/urls.py
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
from django.urls import re_path
|
||||
|
||||
from wagtail.images.views.serve import SendFileView, ServeView
|
||||
from wagtail.test import dummy_sendfile_backend
|
||||
|
||||
urlpatterns = [
|
||||
# Format: signature, image_id, filter_spec, filename=None
|
||||
re_path(
|
||||
r"^actions/serve/(.*)/(\d*)/(.*)/[^/]*",
|
||||
ServeView.as_view(action="serve"),
|
||||
name="wagtailimages_serve_action_serve",
|
||||
),
|
||||
re_path(
|
||||
r"^actions/redirect/(.*)/(\d*)/(.*)/[^/]*",
|
||||
ServeView.as_view(action="redirect"),
|
||||
name="wagtailimages_serve_action_redirect",
|
||||
),
|
||||
re_path(
|
||||
r"^custom_key/(.*)/(\d*)/(.*)/[^/]*",
|
||||
ServeView.as_view(key="custom"),
|
||||
name="wagtailimages_serve_custom_key",
|
||||
),
|
||||
re_path(
|
||||
r"^custom_view/([^/]*)/(\d*)/([^/]*)/[^/]*$",
|
||||
ServeView.as_view(),
|
||||
name="wagtailimages_serve_custom_view",
|
||||
),
|
||||
re_path(
|
||||
r"^sendfile/(.*)/(\d*)/(.*)/[^/]*",
|
||||
SendFileView.as_view(),
|
||||
name="wagtailimages_sendfile",
|
||||
),
|
||||
re_path(
|
||||
r"^sendfile-dummy/(.*)/(\d*)/(.*)/[^/]*",
|
||||
SendFileView.as_view(backend=dummy_sendfile_backend.sendfile),
|
||||
name="wagtailimages_sendfile_dummy",
|
||||
),
|
||||
]
|
||||
93
env/lib/python3.10/site-packages/wagtail/images/tests/utils.py
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
import os
|
||||
from io import BytesIO
|
||||
|
||||
import PIL.Image
|
||||
from django.conf import settings
|
||||
from django.core import serializers
|
||||
from django.core.files.images import ImageFile
|
||||
|
||||
from wagtail.images import get_image_model
|
||||
|
||||
Image = get_image_model()
|
||||
|
||||
|
||||
def get_test_image_filename(image, filterspec):
|
||||
"""
|
||||
Get the generated filename for a resized image
|
||||
"""
|
||||
name, ext = os.path.splitext(os.path.basename(image.file.name))
|
||||
# Use the correct extension if the filterspec is a format operation.
|
||||
if "format-" in filterspec:
|
||||
ext = "." + filterspec.split("format-")[1].split("-")[0].split(".")[0].replace(
|
||||
"jpeg", "jpg"
|
||||
)
|
||||
return f"{settings.MEDIA_URL}images/{name}.{filterspec}{ext}"
|
||||
|
||||
|
||||
def get_test_image_file(filename="test.png", colour="white", size=(640, 480)):
|
||||
f = BytesIO()
|
||||
image = PIL.Image.new("RGBA", size, colour)
|
||||
image.save(f, "PNG")
|
||||
return ImageFile(f, name=filename)
|
||||
|
||||
|
||||
def get_test_image_file_avif(filename="test.png", colour="white", size=(640, 480)):
|
||||
f = BytesIO()
|
||||
image = PIL.Image.new("RGBA", size, colour)
|
||||
image.save(f, "AVIF")
|
||||
return ImageFile(f, name=filename)
|
||||
|
||||
|
||||
def get_test_image_file_jpeg(filename="test.jpg", colour="white", size=(640, 480)):
|
||||
f = BytesIO()
|
||||
image = PIL.Image.new("RGB", size, colour)
|
||||
image.save(f, "JPEG")
|
||||
return ImageFile(f, name=filename)
|
||||
|
||||
|
||||
def get_test_image_file_webp(filename="test.webp", colour="white", size=(640, 480)):
|
||||
f = BytesIO()
|
||||
image = PIL.Image.new("RGB", size, colour)
|
||||
image.save(f, "WEBP")
|
||||
return ImageFile(f, name=filename)
|
||||
|
||||
|
||||
def get_test_image_file_tiff(filename="test.tiff", colour="white", size=(640, 480)):
|
||||
f = BytesIO()
|
||||
image = PIL.Image.new("RGB", size, colour)
|
||||
image.save(f, "TIFF")
|
||||
return ImageFile(f, name=filename)
|
||||
|
||||
|
||||
def get_test_image_file_svg(
|
||||
filename="test.svg", width=100, height=100, view_box="0 0 100 100"
|
||||
):
|
||||
img = f"""
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}" viewBox="{view_box}">
|
||||
</svg>
|
||||
"""
|
||||
f = BytesIO(img.strip().encode("utf-8"))
|
||||
return ImageFile(f, filename)
|
||||
|
||||
|
||||
def get_test_bad_image():
|
||||
# Create an image with a missing file, by deserializing from a python object
|
||||
# (which bypasses FileField's attempt to read the file)
|
||||
return list(
|
||||
serializers.deserialize(
|
||||
"python",
|
||||
[
|
||||
{
|
||||
"fields": {
|
||||
"title": "missing image",
|
||||
"height": 100,
|
||||
"file": "original_images/missing-image.jpg",
|
||||
"width": 100,
|
||||
},
|
||||
"model": "wagtailimages.image",
|
||||
}
|
||||
],
|
||||
)
|
||||
)[0].object
|
||||