mirror of
https://github.com/Hopiu/wagtail.git
synced 2026-04-27 18:14:48 +00:00
Merge branch 'master' into custom-user-model-fixes
This commit is contained in:
commit
fdea93629d
5 changed files with 560 additions and 549 deletions
0
wagtail/wagtailimages/tests/__init__.py
Normal file
0
wagtail/wagtailimages/tests/__init__.py
Normal file
|
|
@ -1,251 +1,15 @@
|
|||
import json
|
||||
import datetime
|
||||
|
||||
from mock import MagicMock
|
||||
import dateutil.parser
|
||||
|
||||
from django.utils import six
|
||||
from django.utils.http import urlquote
|
||||
from django.utils import timezone
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django import template
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Group, Permission
|
||||
from django.utils.http import urlquote
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.db.utils import IntegrityError
|
||||
|
||||
from wagtail.tests.utils import unittest, WagtailTestUtils
|
||||
from wagtail.wagtailimages.models import get_image_model, Rendition
|
||||
from wagtail.wagtailimages.formats import (
|
||||
Format,
|
||||
get_image_format,
|
||||
register_image_format
|
||||
)
|
||||
from wagtail.tests.utils import WagtailTestUtils
|
||||
from wagtail.wagtailimages.utils.crypto import generate_signature
|
||||
|
||||
from wagtail.wagtailimages.backends import get_image_backend
|
||||
from wagtail.wagtailimages.backends.pillow import PillowBackend
|
||||
from wagtail.wagtailimages.utils.crypto import generate_signature, verify_signature
|
||||
from wagtail.tests.models import EventPage, EventPageCarouselItem
|
||||
from wagtail.wagtailcore.models import Page
|
||||
|
||||
|
||||
def get_test_image_file(filename='test.png'):
|
||||
from six import BytesIO
|
||||
from PIL import Image
|
||||
from django.core.files.images import ImageFile
|
||||
|
||||
f = BytesIO()
|
||||
image = Image.new('RGB', (640, 480), 'white')
|
||||
image.save(f, 'PNG')
|
||||
return ImageFile(f, name=filename)
|
||||
|
||||
|
||||
Image = get_image_model()
|
||||
|
||||
|
||||
class TestImage(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_is_portrait(self):
|
||||
self.assertFalse(self.image.is_portrait())
|
||||
|
||||
def test_is_landscape(self):
|
||||
self.assertTrue(self.image.is_landscape())
|
||||
|
||||
|
||||
class TestImagePermissions(TestCase):
|
||||
def setUp(self):
|
||||
# Create some user accounts for testing permissions
|
||||
User = get_user_model()
|
||||
self.user = User.objects.create_user(username='user', email='user@email.com', password='password')
|
||||
self.owner = User.objects.create_user(username='owner', email='owner@email.com', password='password')
|
||||
self.editor = User.objects.create_user(username='editor', email='editor@email.com', password='password')
|
||||
self.editor.groups.add(Group.objects.get(name='Editors'))
|
||||
self.administrator = User.objects.create_superuser(username='administrator', email='administrator@email.com', password='password')
|
||||
|
||||
# Owner user must have the add_image permission
|
||||
self.owner.user_permissions.add(Permission.objects.get(codename='add_image'))
|
||||
|
||||
# Create an image for running tests on
|
||||
self.image = Image.objects.create(
|
||||
title="Test image",
|
||||
uploaded_by_user=self.owner,
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
def test_administrator_can_edit(self):
|
||||
self.assertTrue(self.image.is_editable_by_user(self.administrator))
|
||||
|
||||
def test_editor_can_edit(self):
|
||||
self.assertTrue(self.image.is_editable_by_user(self.editor))
|
||||
|
||||
def test_owner_can_edit(self):
|
||||
self.assertTrue(self.image.is_editable_by_user(self.owner))
|
||||
|
||||
def test_user_cant_edit(self):
|
||||
self.assertFalse(self.image.is_editable_by_user(self.user))
|
||||
|
||||
|
||||
class TestRenditions(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_default_backend(self):
|
||||
# default backend should be pillow
|
||||
backend = get_image_backend()
|
||||
self.assertTrue(isinstance(backend, PillowBackend))
|
||||
|
||||
def test_minification(self):
|
||||
rendition = self.image.get_rendition('width-400')
|
||||
|
||||
# Check size
|
||||
self.assertEqual(rendition.width, 400)
|
||||
self.assertEqual(rendition.height, 300)
|
||||
|
||||
def test_resize_to_max(self):
|
||||
rendition = self.image.get_rendition('max-100x100')
|
||||
|
||||
# Check size
|
||||
self.assertEqual(rendition.width, 100)
|
||||
self.assertEqual(rendition.height, 75)
|
||||
|
||||
|
||||
def test_resize_to_min(self):
|
||||
rendition = self.image.get_rendition('min-120x120')
|
||||
|
||||
# Check size
|
||||
self.assertEqual(rendition.width, 160)
|
||||
self.assertEqual(rendition.height, 120)
|
||||
|
||||
def test_resize_to_original(self):
|
||||
rendition = self.image.get_rendition('original')
|
||||
|
||||
# Check size
|
||||
self.assertEqual(rendition.width, 640)
|
||||
self.assertEqual(rendition.height, 480)
|
||||
|
||||
def test_cache(self):
|
||||
# Get two renditions with the same filter
|
||||
first_rendition = self.image.get_rendition('width-400')
|
||||
second_rendition = self.image.get_rendition('width-400')
|
||||
|
||||
# Check that they are the same object
|
||||
self.assertEqual(first_rendition, second_rendition)
|
||||
|
||||
|
||||
class TestRenditionsWand(TestCase):
|
||||
def setUp(self):
|
||||
try:
|
||||
import wand
|
||||
except ImportError:
|
||||
# skip these tests if Wand is not installed
|
||||
raise unittest.SkipTest(
|
||||
"Skipping image backend tests for wand, as wand is not installed")
|
||||
|
||||
# Create an image for running tests on
|
||||
self.image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
self.image.backend = 'wagtail.wagtailimages.backends.wand.WandBackend'
|
||||
|
||||
def test_minification(self):
|
||||
rendition = self.image.get_rendition('width-400')
|
||||
|
||||
# Check size
|
||||
self.assertEqual(rendition.width, 400)
|
||||
self.assertEqual(rendition.height, 300)
|
||||
|
||||
def test_resize_to_max(self):
|
||||
rendition = self.image.get_rendition('max-100x100')
|
||||
|
||||
# Check size
|
||||
self.assertEqual(rendition.width, 100)
|
||||
self.assertEqual(rendition.height, 75)
|
||||
|
||||
def test_resize_to_min(self):
|
||||
rendition = self.image.get_rendition('min-120x120')
|
||||
|
||||
# Check size
|
||||
self.assertEqual(rendition.width, 160)
|
||||
self.assertEqual(rendition.height, 120)
|
||||
|
||||
def test_resize_to_original(self):
|
||||
rendition = self.image.get_rendition('original')
|
||||
|
||||
# Check size
|
||||
self.assertEqual(rendition.width, 640)
|
||||
self.assertEqual(rendition.height, 480)
|
||||
|
||||
def test_cache(self):
|
||||
# Get two renditions with the same filter
|
||||
first_rendition = self.image.get_rendition('width-400')
|
||||
second_rendition = self.image.get_rendition('width-400')
|
||||
|
||||
# Check that they are the same object
|
||||
self.assertEqual(first_rendition, second_rendition)
|
||||
|
||||
|
||||
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.assertTrue('width="400"' in result)
|
||||
self.assertTrue('height="300"' in result)
|
||||
self.assertTrue('alt="Test image"' in result)
|
||||
|
||||
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.assertTrue('width="400"' in result)
|
||||
self.assertTrue('height="300"' in result)
|
||||
self.assertTrue('alt="Test image"' in 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 %}')
|
||||
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.assertTrue('width="400"' in result)
|
||||
self.assertTrue('height="300"' in result)
|
||||
self.assertTrue('class="photo"' in result)
|
||||
self.assertTrue('title="my wonderful title"' in result)
|
||||
|
||||
## ===== ADMIN VIEWS =====
|
||||
from .utils import Image, get_test_image_file
|
||||
|
||||
|
||||
class TestImageIndexView(TestCase, WagtailTestUtils):
|
||||
|
|
@ -442,99 +206,6 @@ class TestImageChooserUploadView(TestCase, WagtailTestUtils):
|
|||
# TODO: Test uploading through chooser
|
||||
|
||||
|
||||
class TestFormat(TestCase):
|
||||
def setUp(self):
|
||||
# test format
|
||||
self.format = Format(
|
||||
'test name',
|
||||
'test label',
|
||||
'test classnames',
|
||||
'test filter spec'
|
||||
)
|
||||
# test image
|
||||
self.image = MagicMock()
|
||||
self.image.id = 0
|
||||
|
||||
def test_editor_attributes(self):
|
||||
result = self.format.editor_attributes(
|
||||
self.image,
|
||||
'test alt text'
|
||||
)
|
||||
self.assertEqual(result,
|
||||
'data-embedtype="image" data-id="0" data-format="test name" data-alt="test alt text" ')
|
||||
|
||||
def test_image_to_editor_html(self):
|
||||
result = self.format.image_to_editor_html(
|
||||
self.image,
|
||||
'test alt text'
|
||||
)
|
||||
six.assertRegex(self, result,
|
||||
'<img data-embedtype="image" data-id="0" data-format="test name" data-alt="test alt text" class="test classnames" src="[^"]+" width="1" height="1" alt="test alt text">',
|
||||
)
|
||||
|
||||
def test_image_to_html_no_classnames(self):
|
||||
self.format.classnames = None
|
||||
result = self.format.image_to_html(self.image, 'test alt text')
|
||||
six.assertRegex(self, result,
|
||||
'<img src="[^"]+" width="1" height="1" alt="test alt text">'
|
||||
)
|
||||
self.format.classnames = 'test classnames'
|
||||
|
||||
def test_get_image_format(self):
|
||||
register_image_format(self.format)
|
||||
result = get_image_format('test name')
|
||||
self.assertEqual(result, self.format)
|
||||
|
||||
|
||||
class TestUsageCount(TestCase):
|
||||
fixtures = ['wagtail/tests/fixtures/test.json']
|
||||
|
||||
def setUp(self):
|
||||
self.image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
@override_settings(WAGTAIL_USAGE_COUNT_ENABLED=True)
|
||||
def test_unused_image_usage_count(self):
|
||||
self.assertEqual(self.image.get_usage().count(), 0)
|
||||
|
||||
@override_settings(WAGTAIL_USAGE_COUNT_ENABLED=True)
|
||||
def test_used_image_document_usage_count(self):
|
||||
page = EventPage.objects.get(id=4)
|
||||
event_page_carousel_item = EventPageCarouselItem()
|
||||
event_page_carousel_item.page = page
|
||||
event_page_carousel_item.image = self.image
|
||||
event_page_carousel_item.save()
|
||||
self.assertEqual(self.image.get_usage().count(), 1)
|
||||
|
||||
|
||||
class TestGetUsage(TestCase):
|
||||
fixtures = ['wagtail/tests/fixtures/test.json']
|
||||
|
||||
def setUp(self):
|
||||
self.image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
def test_image_get_usage_not_enabled(self):
|
||||
self.assertEqual(list(self.image.get_usage()), [])
|
||||
|
||||
@override_settings(WAGTAIL_USAGE_COUNT_ENABLED=True)
|
||||
def test_unused_image_get_usage(self):
|
||||
self.assertEqual(list(self.image.get_usage()), [])
|
||||
|
||||
@override_settings(WAGTAIL_USAGE_COUNT_ENABLED=True)
|
||||
def test_used_image_document_get_usage(self):
|
||||
page = EventPage.objects.get(id=4)
|
||||
event_page_carousel_item = EventPageCarouselItem()
|
||||
event_page_carousel_item.page = page
|
||||
event_page_carousel_item.image = self.image
|
||||
event_page_carousel_item.save()
|
||||
self.assertTrue(issubclass(Page, type(self.image.get_usage()[0])))
|
||||
|
||||
|
||||
class TestMultipleImageUploader(TestCase, WagtailTestUtils):
|
||||
"""
|
||||
This tests the multiple image upload views located in wagtailimages/views/multiple.py
|
||||
|
|
@ -741,79 +412,6 @@ class TestMultipleImageUploader(TestCase, WagtailTestUtils):
|
|||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
|
||||
class TestSignatureGeneration(TestCase):
|
||||
def test_signature_generation(self):
|
||||
self.assertEqual(generate_signature(100, 'fill-800x600'), b'xnZOzQyUg6pkfciqcfRJRosOrGg=')
|
||||
|
||||
def test_signature_verification(self):
|
||||
self.assertTrue(verify_signature(b'xnZOzQyUg6pkfciqcfRJRosOrGg=', 100, 'fill-800x600'))
|
||||
|
||||
def test_signature_changes_on_image_id(self):
|
||||
self.assertFalse(verify_signature(b'xnZOzQyUg6pkfciqcfRJRosOrGg=', 200, 'fill-800x600'))
|
||||
|
||||
def test_signature_changes_on_filter_spec(self):
|
||||
self.assertFalse(verify_signature(b'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.assertEqual(response['Content-Type'], 'image/jpeg')
|
||||
|
||||
# Make sure the cache headers are set to expire after at least one month
|
||||
self.assertIn('Cache-Control', response)
|
||||
self.assertEqual(response['Cache-Control'].split('=')[0], 'max-age')
|
||||
self.assertTrue(int(response['Cache-Control'].split('=')[1]) > datetime.timedelta(days=30).seconds)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
class TestURLGeneratorView(TestCase, WagtailTestUtils):
|
||||
def setUp(self):
|
||||
# Create an image for running tests on
|
||||
|
|
@ -947,145 +545,3 @@ class TestGenerateURLView(TestCase, WagtailTestUtils):
|
|||
self.assertJSONEqual(response.content.decode(), json.dumps({
|
||||
'error': 'Invalid filter spec.',
|
||||
}))
|
||||
|
||||
|
||||
class TestIssue573(TestCase):
|
||||
"""
|
||||
This tests for a bug which causes filename limit on Renditions to be reached
|
||||
when the Image has a long original filename and a big focal point key
|
||||
"""
|
||||
def test_issue_573(self):
|
||||
# Create an image with a big filename and focal point
|
||||
image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file('thisisaverylongfilename-abcdefghijklmnopqrstuvwxyz-supercalifragilisticexpialidocious.png'),
|
||||
focal_point_x=1000,
|
||||
focal_point_y=1000,
|
||||
focal_point_width=1000,
|
||||
focal_point_height=1000,
|
||||
)
|
||||
|
||||
# Try creating a rendition from that image
|
||||
# This would crash if the bug is present
|
||||
image.get_rendition('fill-800x600')
|
||||
|
||||
|
||||
class TestIssue613(TestCase, WagtailTestUtils):
|
||||
def get_elasticsearch_backend(self):
|
||||
from django.conf import settings
|
||||
from wagtail.wagtailsearch.backends import get_search_backend
|
||||
|
||||
backend_path = 'wagtail.wagtailsearch.backends.elasticsearch.ElasticSearch'
|
||||
|
||||
# Search WAGTAILSEARCH_BACKENDS for an entry that uses the given backend path
|
||||
for backend_name, backend_conf in settings.WAGTAILSEARCH_BACKENDS.items():
|
||||
if backend_conf['BACKEND'] == backend_path:
|
||||
return get_search_backend(backend_name)
|
||||
else:
|
||||
# no conf entry found - skip tests for this backend
|
||||
raise unittest.SkipTest("No WAGTAILSEARCH_BACKENDS entry for the backend %s" % backend_path)
|
||||
|
||||
def setUp(self):
|
||||
self.search_backend = self.get_elasticsearch_backend()
|
||||
self.login()
|
||||
|
||||
def add_image(self, **params):
|
||||
post_data = {
|
||||
'title': "Test image",
|
||||
'file': SimpleUploadedFile('test.png', get_test_image_file().file.getvalue()),
|
||||
}
|
||||
post_data.update(params)
|
||||
response = self.client.post(reverse('wagtailimages_add_image'), post_data)
|
||||
|
||||
# Should redirect back to index
|
||||
self.assertRedirects(response, reverse('wagtailimages_index'))
|
||||
|
||||
# Check that the image was created
|
||||
images = Image.objects.filter(title="Test image")
|
||||
self.assertEqual(images.count(), 1)
|
||||
|
||||
# Test that size was populated correctly
|
||||
image = images.first()
|
||||
self.assertEqual(image.width, 640)
|
||||
self.assertEqual(image.height, 480)
|
||||
|
||||
return image
|
||||
|
||||
def edit_image(self, **params):
|
||||
# Create an image to edit
|
||||
self.image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
# Edit it
|
||||
post_data = {
|
||||
'title': "Edited",
|
||||
}
|
||||
post_data.update(params)
|
||||
response = self.client.post(reverse('wagtailimages_edit_image', args=(self.image.id,)), post_data)
|
||||
|
||||
# Should redirect back to index
|
||||
self.assertRedirects(response, reverse('wagtailimages_index'))
|
||||
|
||||
# Check that the image was edited
|
||||
image = Image.objects.get(id=self.image.id)
|
||||
self.assertEqual(image.title, "Edited")
|
||||
return image
|
||||
|
||||
def test_issue_613_on_add(self):
|
||||
# Reset the search index
|
||||
self.search_backend.reset_index()
|
||||
self.search_backend.add_type(Image)
|
||||
|
||||
# Add an image with some tags
|
||||
image = self.add_image(tags="hello")
|
||||
self.search_backend.refresh_index()
|
||||
|
||||
# Search for it by tag
|
||||
results = self.search_backend.search("hello", Image)
|
||||
|
||||
# Check
|
||||
self.assertEqual(len(results), 1)
|
||||
self.assertEqual(results[0].id, image.id)
|
||||
|
||||
def test_issue_613_on_edit(self):
|
||||
# Reset the search index
|
||||
self.search_backend.reset_index()
|
||||
self.search_backend.add_type(Image)
|
||||
|
||||
# Add an image with some tags
|
||||
image = self.edit_image(tags="hello")
|
||||
self.search_backend.refresh_index()
|
||||
|
||||
# Search for it by tag
|
||||
results = self.search_backend.search("hello", Image)
|
||||
|
||||
# Check
|
||||
self.assertEqual(len(results), 1)
|
||||
self.assertEqual(results[0].id, image.id)
|
||||
|
||||
|
||||
class TestIssue312(TestCase):
|
||||
def test_duplicate_renditions(self):
|
||||
# Create an image
|
||||
image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
# Get two renditions and check that they're the same
|
||||
rend1 = image.get_rendition('fill-100x100')
|
||||
rend2 = image.get_rendition('fill-100x100')
|
||||
self.assertEqual(rend1, rend2)
|
||||
|
||||
# Now manually duplicate the renditon and check that the database blocks it
|
||||
self.assertRaises(
|
||||
IntegrityError,
|
||||
Rendition.objects.create,
|
||||
image=rend1.image,
|
||||
filter=rend1.filter,
|
||||
width=rend1.width,
|
||||
height=rend1.height,
|
||||
focal_point_key=rend1.focal_point_key,
|
||||
)
|
||||
360
wagtail/wagtailimages/tests/test_models.py
Normal file
360
wagtail/wagtailimages/tests/test_models.py
Normal file
|
|
@ -0,0 +1,360 @@
|
|||
from django.test import TestCase
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test.utils import override_settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Group, Permission
|
||||
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.db.utils import IntegrityError
|
||||
|
||||
from wagtail.tests.utils import WagtailTestUtils, unittest
|
||||
from wagtail.wagtailcore.models import Page
|
||||
from wagtail.tests.models import EventPage, EventPageCarouselItem
|
||||
from wagtail.wagtailimages.models import Rendition
|
||||
from wagtail.wagtailimages.backends import get_image_backend
|
||||
from wagtail.wagtailimages.backends.pillow import PillowBackend
|
||||
|
||||
from .utils import Image, get_test_image_file
|
||||
|
||||
|
||||
class TestImage(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_is_portrait(self):
|
||||
self.assertFalse(self.image.is_portrait())
|
||||
|
||||
def test_is_landscape(self):
|
||||
self.assertTrue(self.image.is_landscape())
|
||||
|
||||
|
||||
class TestImagePermissions(TestCase):
|
||||
def setUp(self):
|
||||
# Create some user accounts for testing permissions
|
||||
User = get_user_model()
|
||||
self.user = User.objects.create_user(username='user', email='user@email.com', password='password')
|
||||
self.owner = User.objects.create_user(username='owner', email='owner@email.com', password='password')
|
||||
self.editor = User.objects.create_user(username='editor', email='editor@email.com', password='password')
|
||||
self.editor.groups.add(Group.objects.get(name='Editors'))
|
||||
self.administrator = User.objects.create_superuser(username='administrator', email='administrator@email.com', password='password')
|
||||
|
||||
# Owner user must have the add_image permission
|
||||
self.owner.user_permissions.add(Permission.objects.get(codename='add_image'))
|
||||
|
||||
# Create an image for running tests on
|
||||
self.image = Image.objects.create(
|
||||
title="Test image",
|
||||
uploaded_by_user=self.owner,
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
def test_administrator_can_edit(self):
|
||||
self.assertTrue(self.image.is_editable_by_user(self.administrator))
|
||||
|
||||
def test_editor_can_edit(self):
|
||||
self.assertTrue(self.image.is_editable_by_user(self.editor))
|
||||
|
||||
def test_owner_can_edit(self):
|
||||
self.assertTrue(self.image.is_editable_by_user(self.owner))
|
||||
|
||||
def test_user_cant_edit(self):
|
||||
self.assertFalse(self.image.is_editable_by_user(self.user))
|
||||
|
||||
|
||||
class TestRenditions(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_default_backend(self):
|
||||
# default backend should be pillow
|
||||
backend = get_image_backend()
|
||||
self.assertTrue(isinstance(backend, PillowBackend))
|
||||
|
||||
def test_minification(self):
|
||||
rendition = self.image.get_rendition('width-400')
|
||||
|
||||
# Check size
|
||||
self.assertEqual(rendition.width, 400)
|
||||
self.assertEqual(rendition.height, 300)
|
||||
|
||||
def test_resize_to_max(self):
|
||||
rendition = self.image.get_rendition('max-100x100')
|
||||
|
||||
# Check size
|
||||
self.assertEqual(rendition.width, 100)
|
||||
self.assertEqual(rendition.height, 75)
|
||||
|
||||
|
||||
def test_resize_to_min(self):
|
||||
rendition = self.image.get_rendition('min-120x120')
|
||||
|
||||
# Check size
|
||||
self.assertEqual(rendition.width, 160)
|
||||
self.assertEqual(rendition.height, 120)
|
||||
|
||||
def test_resize_to_original(self):
|
||||
rendition = self.image.get_rendition('original')
|
||||
|
||||
# Check size
|
||||
self.assertEqual(rendition.width, 640)
|
||||
self.assertEqual(rendition.height, 480)
|
||||
|
||||
def test_cache(self):
|
||||
# Get two renditions with the same filter
|
||||
first_rendition = self.image.get_rendition('width-400')
|
||||
second_rendition = self.image.get_rendition('width-400')
|
||||
|
||||
# Check that they are the same object
|
||||
self.assertEqual(first_rendition, second_rendition)
|
||||
|
||||
|
||||
class TestRenditionsWand(TestCase):
|
||||
def setUp(self):
|
||||
try:
|
||||
import wand
|
||||
except ImportError:
|
||||
# skip these tests if Wand is not installed
|
||||
raise unittest.SkipTest(
|
||||
"Skipping image backend tests for wand, as wand is not installed")
|
||||
|
||||
# Create an image for running tests on
|
||||
self.image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
self.image.backend = 'wagtail.wagtailimages.backends.wand.WandBackend'
|
||||
|
||||
def test_minification(self):
|
||||
rendition = self.image.get_rendition('width-400')
|
||||
|
||||
# Check size
|
||||
self.assertEqual(rendition.width, 400)
|
||||
self.assertEqual(rendition.height, 300)
|
||||
|
||||
def test_resize_to_max(self):
|
||||
rendition = self.image.get_rendition('max-100x100')
|
||||
|
||||
# Check size
|
||||
self.assertEqual(rendition.width, 100)
|
||||
self.assertEqual(rendition.height, 75)
|
||||
|
||||
def test_resize_to_min(self):
|
||||
rendition = self.image.get_rendition('min-120x120')
|
||||
|
||||
# Check size
|
||||
self.assertEqual(rendition.width, 160)
|
||||
self.assertEqual(rendition.height, 120)
|
||||
|
||||
def test_resize_to_original(self):
|
||||
rendition = self.image.get_rendition('original')
|
||||
|
||||
# Check size
|
||||
self.assertEqual(rendition.width, 640)
|
||||
self.assertEqual(rendition.height, 480)
|
||||
|
||||
def test_cache(self):
|
||||
# Get two renditions with the same filter
|
||||
first_rendition = self.image.get_rendition('width-400')
|
||||
second_rendition = self.image.get_rendition('width-400')
|
||||
|
||||
# Check that they are the same object
|
||||
self.assertEqual(first_rendition, second_rendition)
|
||||
|
||||
|
||||
class TestUsageCount(TestCase):
|
||||
fixtures = ['wagtail/tests/fixtures/test.json']
|
||||
|
||||
def setUp(self):
|
||||
self.image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
@override_settings(WAGTAIL_USAGE_COUNT_ENABLED=True)
|
||||
def test_unused_image_usage_count(self):
|
||||
self.assertEqual(self.image.get_usage().count(), 0)
|
||||
|
||||
@override_settings(WAGTAIL_USAGE_COUNT_ENABLED=True)
|
||||
def test_used_image_document_usage_count(self):
|
||||
page = EventPage.objects.get(id=4)
|
||||
event_page_carousel_item = EventPageCarouselItem()
|
||||
event_page_carousel_item.page = page
|
||||
event_page_carousel_item.image = self.image
|
||||
event_page_carousel_item.save()
|
||||
self.assertEqual(self.image.get_usage().count(), 1)
|
||||
|
||||
|
||||
class TestGetUsage(TestCase):
|
||||
fixtures = ['wagtail/tests/fixtures/test.json']
|
||||
|
||||
def setUp(self):
|
||||
self.image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
def test_image_get_usage_not_enabled(self):
|
||||
self.assertEqual(list(self.image.get_usage()), [])
|
||||
|
||||
@override_settings(WAGTAIL_USAGE_COUNT_ENABLED=True)
|
||||
def test_unused_image_get_usage(self):
|
||||
self.assertEqual(list(self.image.get_usage()), [])
|
||||
|
||||
@override_settings(WAGTAIL_USAGE_COUNT_ENABLED=True)
|
||||
def test_used_image_document_get_usage(self):
|
||||
page = EventPage.objects.get(id=4)
|
||||
event_page_carousel_item = EventPageCarouselItem()
|
||||
event_page_carousel_item.page = page
|
||||
event_page_carousel_item.image = self.image
|
||||
event_page_carousel_item.save()
|
||||
self.assertTrue(issubclass(Page, type(self.image.get_usage()[0])))
|
||||
|
||||
|
||||
class TestIssue573(TestCase):
|
||||
"""
|
||||
This tests for a bug which causes filename limit on Renditions to be reached
|
||||
when the Image has a long original filename and a big focal point key
|
||||
"""
|
||||
def test_issue_573(self):
|
||||
# Create an image with a big filename and focal point
|
||||
image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file('thisisaverylongfilename-abcdefghijklmnopqrstuvwxyz-supercalifragilisticexpialidocious.png'),
|
||||
focal_point_x=1000,
|
||||
focal_point_y=1000,
|
||||
focal_point_width=1000,
|
||||
focal_point_height=1000,
|
||||
)
|
||||
|
||||
# Try creating a rendition from that image
|
||||
# This would crash if the bug is present
|
||||
image.get_rendition('fill-800x600')
|
||||
|
||||
|
||||
class TestIssue613(TestCase, WagtailTestUtils):
|
||||
def get_elasticsearch_backend(self):
|
||||
from django.conf import settings
|
||||
from wagtail.wagtailsearch.backends import get_search_backend
|
||||
|
||||
backend_path = 'wagtail.wagtailsearch.backends.elasticsearch.ElasticSearch'
|
||||
|
||||
# Search WAGTAILSEARCH_BACKENDS for an entry that uses the given backend path
|
||||
for backend_name, backend_conf in settings.WAGTAILSEARCH_BACKENDS.items():
|
||||
if backend_conf['BACKEND'] == backend_path:
|
||||
return get_search_backend(backend_name)
|
||||
else:
|
||||
# no conf entry found - skip tests for this backend
|
||||
raise unittest.SkipTest("No WAGTAILSEARCH_BACKENDS entry for the backend %s" % backend_path)
|
||||
|
||||
def setUp(self):
|
||||
self.search_backend = self.get_elasticsearch_backend()
|
||||
self.login()
|
||||
|
||||
def add_image(self, **params):
|
||||
post_data = {
|
||||
'title': "Test image",
|
||||
'file': SimpleUploadedFile('test.png', get_test_image_file().file.getvalue()),
|
||||
}
|
||||
post_data.update(params)
|
||||
response = self.client.post(reverse('wagtailimages_add_image'), post_data)
|
||||
|
||||
# Should redirect back to index
|
||||
self.assertRedirects(response, reverse('wagtailimages_index'))
|
||||
|
||||
# Check that the image was created
|
||||
images = Image.objects.filter(title="Test image")
|
||||
self.assertEqual(images.count(), 1)
|
||||
|
||||
# Test that size was populated correctly
|
||||
image = images.first()
|
||||
self.assertEqual(image.width, 640)
|
||||
self.assertEqual(image.height, 480)
|
||||
|
||||
return image
|
||||
|
||||
def edit_image(self, **params):
|
||||
# Create an image to edit
|
||||
self.image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
# Edit it
|
||||
post_data = {
|
||||
'title': "Edited",
|
||||
}
|
||||
post_data.update(params)
|
||||
response = self.client.post(reverse('wagtailimages_edit_image', args=(self.image.id,)), post_data)
|
||||
|
||||
# Should redirect back to index
|
||||
self.assertRedirects(response, reverse('wagtailimages_index'))
|
||||
|
||||
# Check that the image was edited
|
||||
image = Image.objects.get(id=self.image.id)
|
||||
self.assertEqual(image.title, "Edited")
|
||||
return image
|
||||
|
||||
def test_issue_613_on_add(self):
|
||||
# Reset the search index
|
||||
self.search_backend.reset_index()
|
||||
self.search_backend.add_type(Image)
|
||||
|
||||
# Add an image with some tags
|
||||
image = self.add_image(tags="hello")
|
||||
self.search_backend.refresh_index()
|
||||
|
||||
# Search for it by tag
|
||||
results = self.search_backend.search("hello", Image)
|
||||
|
||||
# Check
|
||||
self.assertEqual(len(results), 1)
|
||||
self.assertEqual(results[0].id, image.id)
|
||||
|
||||
def test_issue_613_on_edit(self):
|
||||
# Reset the search index
|
||||
self.search_backend.reset_index()
|
||||
self.search_backend.add_type(Image)
|
||||
|
||||
# Add an image with some tags
|
||||
image = self.edit_image(tags="hello")
|
||||
self.search_backend.refresh_index()
|
||||
|
||||
# Search for it by tag
|
||||
results = self.search_backend.search("hello", Image)
|
||||
|
||||
# Check
|
||||
self.assertEqual(len(results), 1)
|
||||
self.assertEqual(results[0].id, image.id)
|
||||
|
||||
|
||||
class TestIssue312(TestCase):
|
||||
def test_duplicate_renditions(self):
|
||||
# Create an image
|
||||
image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
# Get two renditions and check that they're the same
|
||||
rend1 = image.get_rendition('fill-100x100')
|
||||
rend2 = image.get_rendition('fill-100x100')
|
||||
self.assertEqual(rend1, rend2)
|
||||
|
||||
# Now manually duplicate the renditon and check that the database blocks it
|
||||
self.assertRaises(
|
||||
IntegrityError,
|
||||
Rendition.objects.create,
|
||||
image=rend1.image,
|
||||
filter=rend1.filter,
|
||||
width=rend1.width,
|
||||
height=rend1.height,
|
||||
focal_point_key=rend1.focal_point_key,
|
||||
)
|
||||
179
wagtail/wagtailimages/tests/tests.py
Normal file
179
wagtail/wagtailimages/tests/tests.py
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
import datetime
|
||||
|
||||
from mock import MagicMock
|
||||
|
||||
from django.test import TestCase
|
||||
from django import template
|
||||
from django.utils import six
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from wagtail.wagtailimages.utils.crypto import generate_signature, verify_signature
|
||||
from wagtail.wagtailimages.formats import Format, get_image_format, register_image_format
|
||||
|
||||
from .utils import Image, get_test_image_file
|
||||
|
||||
|
||||
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.assertTrue('width="400"' in result)
|
||||
self.assertTrue('height="300"' in result)
|
||||
self.assertTrue('alt="Test image"' in result)
|
||||
|
||||
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.assertTrue('width="400"' in result)
|
||||
self.assertTrue('height="300"' in result)
|
||||
self.assertTrue('alt="Test image"' in 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 %}')
|
||||
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.assertTrue('width="400"' in result)
|
||||
self.assertTrue('height="300"' in result)
|
||||
self.assertTrue('class="photo"' in result)
|
||||
self.assertTrue('title="my wonderful title"' in result)
|
||||
|
||||
|
||||
class TestFormat(TestCase):
|
||||
def setUp(self):
|
||||
# test format
|
||||
self.format = Format(
|
||||
'test name',
|
||||
'test label',
|
||||
'test classnames',
|
||||
'test filter spec'
|
||||
)
|
||||
# test image
|
||||
self.image = MagicMock()
|
||||
self.image.id = 0
|
||||
|
||||
def test_editor_attributes(self):
|
||||
result = self.format.editor_attributes(
|
||||
self.image,
|
||||
'test alt text'
|
||||
)
|
||||
self.assertEqual(result,
|
||||
'data-embedtype="image" data-id="0" data-format="test name" data-alt="test alt text" ')
|
||||
|
||||
def test_image_to_editor_html(self):
|
||||
result = self.format.image_to_editor_html(
|
||||
self.image,
|
||||
'test alt text'
|
||||
)
|
||||
six.assertRegex(self, result,
|
||||
'<img data-embedtype="image" data-id="0" data-format="test name" data-alt="test alt text" class="test classnames" src="[^"]+" width="1" height="1" alt="test alt text">',
|
||||
)
|
||||
|
||||
def test_image_to_html_no_classnames(self):
|
||||
self.format.classnames = None
|
||||
result = self.format.image_to_html(self.image, 'test alt text')
|
||||
six.assertRegex(self, result,
|
||||
'<img src="[^"]+" width="1" height="1" alt="test alt text">'
|
||||
)
|
||||
self.format.classnames = 'test classnames'
|
||||
|
||||
def test_get_image_format(self):
|
||||
register_image_format(self.format)
|
||||
result = get_image_format('test name')
|
||||
self.assertEqual(result, self.format)
|
||||
|
||||
|
||||
class TestSignatureGeneration(TestCase):
|
||||
def test_signature_generation(self):
|
||||
self.assertEqual(generate_signature(100, 'fill-800x600'), b'xnZOzQyUg6pkfciqcfRJRosOrGg=')
|
||||
|
||||
def test_signature_verification(self):
|
||||
self.assertTrue(verify_signature(b'xnZOzQyUg6pkfciqcfRJRosOrGg=', 100, 'fill-800x600'))
|
||||
|
||||
def test_signature_changes_on_image_id(self):
|
||||
self.assertFalse(verify_signature(b'xnZOzQyUg6pkfciqcfRJRosOrGg=', 200, 'fill-800x600'))
|
||||
|
||||
def test_signature_changes_on_filter_spec(self):
|
||||
self.assertFalse(verify_signature(b'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.assertEqual(response['Content-Type'], 'image/jpeg')
|
||||
|
||||
# Make sure the cache headers are set to expire after at least one month
|
||||
self.assertIn('Cache-Control', response)
|
||||
self.assertEqual(response['Cache-Control'].split('=')[0], 'max-age')
|
||||
self.assertTrue(int(response['Cache-Control'].split('=')[1]) > datetime.timedelta(days=30).seconds)
|
||||
|
||||
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)
|
||||
16
wagtail/wagtailimages/tests/utils.py
Normal file
16
wagtail/wagtailimages/tests/utils.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import PIL.Image
|
||||
from six import BytesIO
|
||||
|
||||
from django.core.files.images import ImageFile
|
||||
|
||||
from wagtail.wagtailimages.models import get_image_model
|
||||
|
||||
|
||||
Image = get_image_model()
|
||||
|
||||
|
||||
def get_test_image_file(filename='test.png'):
|
||||
f = BytesIO()
|
||||
image = PIL.Image.new('RGB', (640, 480), 'white')
|
||||
image.save(f, 'PNG')
|
||||
return ImageFile(f, name=filename)
|
||||
Loading…
Reference in a new issue