Initial commit
This commit is contained in:
0
env/lib/python3.10/site-packages/wagtail/snippets/tests/__init__.py
vendored
Normal file
0
env/lib/python3.10/site-packages/wagtail/snippets/tests/__init__.py
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/snippets/tests/__pycache__/__init__.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/snippets/tests/__pycache__/__init__.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/snippets/tests/__pycache__/test_locking.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/snippets/tests/__pycache__/test_locking.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/snippets/tests/__pycache__/test_management.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/snippets/tests/__pycache__/test_management.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/snippets/tests/__pycache__/test_preview.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/snippets/tests/__pycache__/test_preview.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/snippets/tests/__pycache__/test_snippets.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/snippets/tests/__pycache__/test_snippets.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/snippets/tests/__pycache__/test_usage.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/snippets/tests/__pycache__/test_usage.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/snippets/tests/__pycache__/test_viewset.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/snippets/tests/__pycache__/test_viewset.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/snippets/tests/__pycache__/test_workflows.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/snippets/tests/__pycache__/test_workflows.cpython-310.pyc
vendored
Normal file
Binary file not shown.
0
env/lib/python3.10/site-packages/wagtail/snippets/tests/test_bulk_actions/__init__.py
vendored
Normal file
0
env/lib/python3.10/site-packages/wagtail/snippets/tests/test_bulk_actions/__init__.py
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
241
env/lib/python3.10/site-packages/wagtail/snippets/tests/test_bulk_actions/test_bulk_delete.py
vendored
Normal file
241
env/lib/python3.10/site-packages/wagtail/snippets/tests/test_bulk_actions/test_bulk_delete.py
vendored
Normal file
@@ -0,0 +1,241 @@
|
||||
from django.contrib.admin.utils import quote
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from wagtail.snippets.bulk_actions.delete import DeleteBulkAction
|
||||
from wagtail.test.testapp.models import FullFeaturedSnippet
|
||||
from wagtail.test.utils import WagtailTestUtils
|
||||
|
||||
|
||||
class TestSnippetDeleteView(WagtailTestUtils, TestCase):
|
||||
def setUp(self):
|
||||
self.snippet_model = FullFeaturedSnippet
|
||||
|
||||
# create a set of test snippets
|
||||
self.test_snippets = [
|
||||
self.snippet_model.objects.create(
|
||||
text=f"Title-{i}",
|
||||
)
|
||||
for i in range(1, 6)
|
||||
]
|
||||
|
||||
self.user = self.login()
|
||||
self.url = (
|
||||
reverse(
|
||||
"wagtail_bulk_action",
|
||||
args=(
|
||||
self.snippet_model._meta.app_label,
|
||||
self.snippet_model._meta.model_name,
|
||||
"delete",
|
||||
),
|
||||
)
|
||||
+ "?"
|
||||
)
|
||||
|
||||
def get_url(self, items=()):
|
||||
items = items or self.test_snippets
|
||||
return self.url + "&".join(f"id={item.pk}" for item in items)
|
||||
|
||||
def test_simple(self):
|
||||
response = self.client.get(self.get_url())
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(
|
||||
response, "wagtailsnippets/bulk_actions/confirm_bulk_delete.html"
|
||||
)
|
||||
self.assertTemplateUsed(response, "wagtailadmin/shared/header.html")
|
||||
self.assertEqual(response.context["header_icon"], "cog")
|
||||
self.assertContains(response, "icon icon-cog", count=1)
|
||||
|
||||
def test_get_single_delete(self):
|
||||
item = self.test_snippets[0]
|
||||
response = self.client.get(self.get_url(items=(item,)))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(
|
||||
response, "wagtailsnippets/bulk_actions/confirm_bulk_delete.html"
|
||||
)
|
||||
self.assertTemplateUsed(response, "wagtailadmin/shared/header.html")
|
||||
self.assertEqual(response.context["header_icon"], "cog")
|
||||
self.assertContains(response, "icon icon-cog", count=1)
|
||||
self.assertContains(
|
||||
response,
|
||||
"<title>Delete full-featured snippet - Title-1 - Wagtail</title>",
|
||||
html=True,
|
||||
)
|
||||
self.assertContains(
|
||||
response,
|
||||
reverse(
|
||||
self.snippet_model.snippet_viewset.get_url_name("usage"),
|
||||
args=(quote(item.pk),),
|
||||
),
|
||||
)
|
||||
self.assertContains(response, "Used 0 times")
|
||||
|
||||
def test_bulk_delete(self):
|
||||
response = self.client.post(self.get_url())
|
||||
|
||||
# Should redirect back to index
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
# Check that the users were deleted
|
||||
for snippet in self.test_snippets:
|
||||
self.assertFalse(self.snippet_model.objects.filter(pk=snippet.pk).exists())
|
||||
|
||||
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.get_url())
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
html = response.content.decode()
|
||||
self.assertInHTML(
|
||||
"<p>You don't have permission to delete these full-featured snippets</p>",
|
||||
html,
|
||||
)
|
||||
|
||||
for snippet in self.test_snippets:
|
||||
self.assertInHTML(f"<li>{snippet.text}</li>", html)
|
||||
|
||||
response = self.client.post(self.get_url())
|
||||
# User should be redirected back to the index
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
# Snippets should not be deleted
|
||||
for snippet in self.test_snippets:
|
||||
self.assertTrue(self.snippet_model.objects.filter(pk=snippet.pk).exists())
|
||||
|
||||
def test_before_bulk_action_hook_get(self):
|
||||
with self.register_hook(
|
||||
"before_bulk_action", lambda *args: HttpResponse("Overridden!")
|
||||
):
|
||||
response = self.client.get(self.get_url())
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# The hook was not called
|
||||
self.assertNotEqual(response.content, b"Overridden!")
|
||||
|
||||
# The instances were not deleted
|
||||
self.assertQuerySetEqual(
|
||||
self.snippet_model.objects.filter(
|
||||
pk__in=[snippet.pk for snippet in self.test_snippets]
|
||||
),
|
||||
self.test_snippets,
|
||||
ordered=False,
|
||||
)
|
||||
|
||||
def test_before_bulk_action_hook_post(self):
|
||||
def hook_func(request, action_type, instances, action_class_instance):
|
||||
self.assertIsInstance(request, HttpRequest)
|
||||
self.assertEqual(action_type, "delete")
|
||||
self.assertEqual(set(instances), set(self.test_snippets))
|
||||
self.assertIsInstance(action_class_instance, DeleteBulkAction)
|
||||
return HttpResponse("Overridden!")
|
||||
|
||||
with self.register_hook("before_bulk_action", hook_func):
|
||||
response = self.client.post(self.get_url())
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.content, b"Overridden!")
|
||||
|
||||
# Request intercepted before the snippets were deleted
|
||||
self.assertQuerySetEqual(
|
||||
self.snippet_model.objects.filter(
|
||||
pk__in=[snippet.pk for snippet in self.test_snippets]
|
||||
),
|
||||
self.test_snippets,
|
||||
ordered=False,
|
||||
)
|
||||
|
||||
def test_after_bulk_action_hook(self):
|
||||
def hook_func(request, action_type, instances, action_class_instance):
|
||||
self.assertIsInstance(request, HttpRequest)
|
||||
self.assertEqual(action_type, "delete")
|
||||
self.assertEqual(set(instances), set(self.test_snippets))
|
||||
self.assertIsInstance(action_class_instance, DeleteBulkAction)
|
||||
return HttpResponse("Overridden!")
|
||||
|
||||
with self.register_hook("after_bulk_action", hook_func):
|
||||
response = self.client.post(self.get_url())
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.content, b"Overridden!")
|
||||
|
||||
# Request intercepted after the snippets were deleted
|
||||
self.assertFalse(
|
||||
self.snippet_model.objects.filter(
|
||||
pk__in=[snippet.pk for snippet in self.test_snippets]
|
||||
).exists()
|
||||
)
|
||||
|
||||
# Also tests that the {before,after}_delete_snippet hooks are called.
|
||||
# These hooks have existed since before bulk actions were introduced,
|
||||
# so we need to make sure they still work.
|
||||
|
||||
def test_before_delete_snippet_hook_get(self):
|
||||
def hook_func(request, instances):
|
||||
self.assertIsInstance(request, HttpRequest)
|
||||
self.assertEqual(set(instances), set(self.test_snippets))
|
||||
return HttpResponse("Overridden!")
|
||||
|
||||
with self.register_hook("before_delete_snippet", hook_func):
|
||||
response = self.client.get(self.get_url())
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.content, b"Overridden!")
|
||||
|
||||
# Request intercepted before the snippets were deleted
|
||||
self.assertQuerySetEqual(
|
||||
self.snippet_model.objects.filter(
|
||||
pk__in=[snippet.pk for snippet in self.test_snippets]
|
||||
),
|
||||
self.test_snippets,
|
||||
ordered=False,
|
||||
)
|
||||
|
||||
def test_before_delete_snippet_hook_post(self):
|
||||
def hook_func(request, instances):
|
||||
self.assertIsInstance(request, HttpRequest)
|
||||
self.assertEqual(set(instances), set(self.test_snippets))
|
||||
return HttpResponse("Overridden!")
|
||||
|
||||
with self.register_hook("before_delete_snippet", hook_func):
|
||||
response = self.client.post(self.get_url())
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.content, b"Overridden!")
|
||||
|
||||
# Request intercepted before the snippets were deleted
|
||||
self.assertQuerySetEqual(
|
||||
self.snippet_model.objects.filter(
|
||||
pk__in=[snippet.pk for snippet in self.test_snippets]
|
||||
),
|
||||
self.test_snippets,
|
||||
ordered=False,
|
||||
)
|
||||
|
||||
def test_after_delete_snippet_hook(self):
|
||||
def hook_func(request, instances):
|
||||
self.assertIsInstance(request, HttpRequest)
|
||||
self.assertEqual(set(instances), set(self.test_snippets))
|
||||
return HttpResponse("Overridden!")
|
||||
|
||||
with self.register_hook("after_delete_snippet", hook_func):
|
||||
response = self.client.post(self.get_url())
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.content, b"Overridden!")
|
||||
|
||||
# Request intercepted after the snippets were deleted
|
||||
self.assertFalse(
|
||||
self.snippet_model.objects.filter(
|
||||
pk__in=[snippet.pk for snippet in self.test_snippets]
|
||||
).exists()
|
||||
)
|
||||
56
env/lib/python3.10/site-packages/wagtail/snippets/tests/test_bulk_actions/test_custom_models.py
vendored
Normal file
56
env/lib/python3.10/site-packages/wagtail/snippets/tests/test_bulk_actions/test_custom_models.py
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from wagtail.test.testapp.models import Advert, FullFeaturedSnippet
|
||||
from wagtail.test.utils.wagtail_tests import WagtailTestUtils
|
||||
|
||||
|
||||
class TestCustomModels(WagtailTestUtils, TestCase):
|
||||
def setUp(self):
|
||||
self.user = self.login()
|
||||
|
||||
def create_snippets(self, model):
|
||||
return [model.objects.create(text=f"Title-{i}") for i in range(1, 6)]
|
||||
|
||||
def get_action_url(self, model, snippets):
|
||||
return (
|
||||
reverse(
|
||||
"wagtail_bulk_action",
|
||||
args=(
|
||||
model._meta.app_label,
|
||||
model._meta.model_name,
|
||||
"disable",
|
||||
),
|
||||
)
|
||||
+ "?"
|
||||
+ "&".join(f"id={item.pk}" for item in snippets)
|
||||
)
|
||||
|
||||
def get_list_url(self, model):
|
||||
return reverse(model.snippet_viewset.get_url_name("list"))
|
||||
|
||||
def test_action_shown_for_custom_models(self):
|
||||
self.create_snippets(FullFeaturedSnippet)
|
||||
response = self.client.get(self.get_list_url(FullFeaturedSnippet))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, "Disable selected full-featured snippets")
|
||||
|
||||
def test_action_confirmation_accessible_for_custom_models(self):
|
||||
snippets = self.create_snippets(FullFeaturedSnippet)
|
||||
response = self.client.get(self.get_action_url(FullFeaturedSnippet, snippets))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(
|
||||
response,
|
||||
"wagtailadmin/bulk_actions/confirmation/base.html",
|
||||
)
|
||||
|
||||
def test_action_not_shown_for_other_models(self):
|
||||
self.create_snippets(Advert)
|
||||
response = self.client.get(self.get_list_url(Advert))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertNotContains(response, "Disable selected full-featured snippets")
|
||||
|
||||
def test_action_confirmation_inaccessible_for_other_models(self):
|
||||
snippets = self.create_snippets(Advert)
|
||||
response = self.client.get(self.get_action_url(Advert, snippets))
|
||||
self.assertEqual(response.status_code, 404)
|
||||
733
env/lib/python3.10/site-packages/wagtail/snippets/tests/test_locking.py
vendored
Normal file
733
env/lib/python3.10/site-packages/wagtail/snippets/tests/test_locking.py
vendored
Normal file
@@ -0,0 +1,733 @@
|
||||
from django.contrib.admin.utils import quote
|
||||
from django.contrib.auth import get_permission_codename
|
||||
from django.contrib.auth.models import Group, Permission
|
||||
from django.test import TestCase, override_settings
|
||||
from django.urls import NoReverseMatch, reverse
|
||||
from django.utils import timezone
|
||||
|
||||
from wagtail.admin.utils import get_user_display_name
|
||||
from wagtail.locks import WorkflowLock
|
||||
from wagtail.models import GroupApprovalTask, Workflow, WorkflowTask
|
||||
from wagtail.test.testapp.models import (
|
||||
Advert,
|
||||
DraftStateModel,
|
||||
FullFeaturedSnippet,
|
||||
LockableModel,
|
||||
)
|
||||
from wagtail.test.utils import WagtailTestUtils
|
||||
|
||||
|
||||
class BaseLockingTestCase(WagtailTestUtils, TestCase):
|
||||
model = LockableModel
|
||||
|
||||
def setUp(self):
|
||||
self.user = self.login()
|
||||
self.snippet = self.model.objects.create(text="I'm a lockable snippet!")
|
||||
|
||||
@property
|
||||
def model_name(self):
|
||||
return self.model._meta.verbose_name
|
||||
|
||||
def get_url(self, name, args=None):
|
||||
args = args if args is not None else [quote(self.snippet.pk)]
|
||||
return reverse(self.snippet.snippet_viewset.get_url_name(name), args=args)
|
||||
|
||||
def lock_snippet(self, user=None):
|
||||
self.snippet.locked = True
|
||||
self.snippet.locked_by = user
|
||||
self.snippet.locked_at = timezone.now()
|
||||
self.snippet.save()
|
||||
|
||||
def refresh_snippet(self):
|
||||
self.snippet.refresh_from_db()
|
||||
|
||||
def set_permissions(self, permission_names, user=None):
|
||||
if user is None:
|
||||
user = self.user
|
||||
|
||||
user.is_superuser = False
|
||||
|
||||
permissions = [
|
||||
Permission.objects.get(
|
||||
content_type__app_label="wagtailadmin",
|
||||
codename="access_admin",
|
||||
)
|
||||
]
|
||||
|
||||
for name in permission_names:
|
||||
permissions.append(
|
||||
Permission.objects.get(
|
||||
content_type__app_label="tests",
|
||||
codename=get_permission_codename(name, self.model._meta),
|
||||
)
|
||||
)
|
||||
|
||||
user.user_permissions.set(permissions)
|
||||
user.save()
|
||||
|
||||
|
||||
class DraftStateModelTestCase:
|
||||
model = DraftStateModel
|
||||
|
||||
def refresh_snippet(self):
|
||||
self.snippet.refresh_from_db()
|
||||
self.snippet = self.snippet.get_latest_revision_as_object()
|
||||
|
||||
|
||||
class TestLocking(BaseLockingTestCase):
|
||||
def test_lock_post(self):
|
||||
response = self.client.post(self.get_url("lock"))
|
||||
self.refresh_snippet()
|
||||
|
||||
# Check response
|
||||
self.assertRedirects(response, self.get_url("edit"))
|
||||
|
||||
# Check that the snippet is locked
|
||||
self.assertTrue(self.snippet.locked)
|
||||
self.assertEqual(self.snippet.locked_by, self.user)
|
||||
self.assertIsNotNone(self.snippet.locked_at)
|
||||
|
||||
def test_lock_get(self):
|
||||
response = self.client.get(self.get_url("lock"))
|
||||
self.refresh_snippet()
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 405)
|
||||
|
||||
# Check that the snippet is still unlocked
|
||||
self.assertFalse(self.snippet.locked)
|
||||
self.assertIsNone(self.snippet.locked_by)
|
||||
self.assertIsNone(self.snippet.locked_at)
|
||||
|
||||
def test_lock_post_already_locked(self):
|
||||
# Lock the snippet
|
||||
self.lock_snippet(self.user)
|
||||
|
||||
response = self.client.post(self.get_url("lock"))
|
||||
self.refresh_snippet()
|
||||
|
||||
# Check response
|
||||
self.assertRedirects(response, self.get_url("edit"))
|
||||
|
||||
# Check that the snippet is still locked
|
||||
self.assertTrue(self.snippet.locked)
|
||||
self.assertEqual(self.snippet.locked_by, self.user)
|
||||
self.assertIsNotNone(self.snippet.locked_at)
|
||||
|
||||
def test_lock_post_with_good_redirect(self):
|
||||
next_url = self.get_url("list", args=[])
|
||||
response = self.client.post(self.get_url("lock"), {"next": next_url})
|
||||
self.refresh_snippet()
|
||||
|
||||
# Check response
|
||||
self.assertRedirects(response, next_url)
|
||||
|
||||
# Check that the snippet is locked
|
||||
self.assertTrue(self.snippet.locked)
|
||||
self.assertEqual(self.snippet.locked_by, self.user)
|
||||
self.assertIsNotNone(self.snippet.locked_at)
|
||||
|
||||
def test_lock_post_with_bad_redirect(self):
|
||||
response = self.client.post(
|
||||
self.get_url("lock"),
|
||||
{"next": "http://www.google.co.uk"},
|
||||
)
|
||||
self.refresh_snippet()
|
||||
|
||||
# Check response
|
||||
self.assertRedirects(response, self.get_url("edit"))
|
||||
|
||||
# Check that the snippet is locked
|
||||
self.assertTrue(self.snippet.locked)
|
||||
self.assertEqual(self.snippet.locked_by, self.user)
|
||||
self.assertIsNotNone(self.snippet.locked_at)
|
||||
|
||||
def test_lock_post_bad_snippet(self):
|
||||
response = self.client.post(self.get_url("edit", args=[quote(9999999)]))
|
||||
self.refresh_snippet()
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
# Check that the snippet is still unlocked
|
||||
self.assertFalse(self.snippet.locked)
|
||||
self.assertIsNone(self.snippet.locked_by)
|
||||
self.assertIsNone(self.snippet.locked_at)
|
||||
|
||||
def test_lock_post_not_enabled_snippet(self):
|
||||
self.snippet = Advert.objects.create(text="I'm a non-lockable snippet!")
|
||||
|
||||
with self.assertRaises(NoReverseMatch):
|
||||
self.client.post(self.get_url("lock"))
|
||||
|
||||
def test_lock_post_bad_permissions(self):
|
||||
# Remove privileges from user
|
||||
self.set_permissions([])
|
||||
|
||||
response = self.client.post(self.get_url("lock"))
|
||||
self.refresh_snippet()
|
||||
|
||||
# Check response
|
||||
self.assertRedirects(response, reverse("wagtailadmin_home"))
|
||||
|
||||
# Check that the snippet is still unlocked
|
||||
self.assertFalse(self.snippet.locked)
|
||||
self.assertIsNone(self.snippet.locked_by)
|
||||
self.assertIsNone(self.snippet.locked_at)
|
||||
|
||||
def test_unlock_post(self):
|
||||
# Lock the snippet
|
||||
self.lock_snippet(self.user)
|
||||
|
||||
response = self.client.post(self.get_url("unlock"))
|
||||
self.refresh_snippet()
|
||||
|
||||
# Check response
|
||||
self.assertRedirects(response, self.get_url("edit"))
|
||||
|
||||
# Check that the snippet is unlocked
|
||||
self.assertFalse(self.snippet.locked)
|
||||
self.assertIsNone(self.snippet.locked_by)
|
||||
self.assertIsNone(self.snippet.locked_at)
|
||||
|
||||
def test_unlock_get(self):
|
||||
# Lock the snippet
|
||||
self.lock_snippet(self.user)
|
||||
|
||||
response = self.client.get(self.get_url("unlock"))
|
||||
self.refresh_snippet()
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 405)
|
||||
|
||||
# Check that the snippet is still locked
|
||||
self.assertTrue(self.snippet.locked)
|
||||
self.assertEqual(self.snippet.locked_by, self.user)
|
||||
self.assertIsNotNone(self.snippet.locked_at)
|
||||
|
||||
def test_unlock_post_already_unlocked(self):
|
||||
response = self.client.post(self.get_url("unlock"))
|
||||
self.refresh_snippet()
|
||||
|
||||
# Check response
|
||||
self.assertRedirects(response, self.get_url("edit"))
|
||||
|
||||
# Check that the snippet is still unlocked
|
||||
self.assertFalse(self.snippet.locked)
|
||||
self.assertIsNone(self.snippet.locked_by)
|
||||
self.assertIsNone(self.snippet.locked_at)
|
||||
|
||||
def test_unlock_post_with_good_redirect(self):
|
||||
# Lock the snippet
|
||||
self.lock_snippet(self.user)
|
||||
|
||||
next_url = self.get_url("list", args=[])
|
||||
response = self.client.post(self.get_url("unlock"), {"next": next_url})
|
||||
self.refresh_snippet()
|
||||
|
||||
# Check response
|
||||
self.assertRedirects(response, next_url)
|
||||
|
||||
# Check that the snippet is unlocked
|
||||
self.assertFalse(self.snippet.locked)
|
||||
self.assertIsNone(self.snippet.locked_by)
|
||||
self.assertIsNone(self.snippet.locked_at)
|
||||
|
||||
def test_unlock_post_with_bad_redirect(self):
|
||||
# Lock the snippet
|
||||
self.lock_snippet(self.user)
|
||||
|
||||
response = self.client.post(
|
||||
self.get_url("unlock"),
|
||||
{"next": "http://www.google.co.uk"},
|
||||
)
|
||||
self.refresh_snippet()
|
||||
|
||||
# Check response
|
||||
self.assertRedirects(response, self.get_url("edit"))
|
||||
|
||||
# Check that the snippet is unlocked
|
||||
self.assertFalse(self.snippet.locked)
|
||||
self.assertIsNone(self.snippet.locked_by)
|
||||
self.assertIsNone(self.snippet.locked_at)
|
||||
|
||||
def test_unlock_post_bad_snippet(self):
|
||||
# Lock the snippet
|
||||
self.lock_snippet(self.user)
|
||||
|
||||
response = self.client.post(self.get_url("unlock", args=[quote(9999999)]))
|
||||
self.refresh_snippet()
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
# Check that the snippet is still locked
|
||||
self.assertTrue(self.snippet.locked)
|
||||
self.assertEqual(self.snippet.locked_by, self.user)
|
||||
self.assertIsNotNone(self.snippet.locked_at)
|
||||
|
||||
def test_unlock_post_not_enabled_snippet(self):
|
||||
self.snippet = Advert.objects.create(text="I'm a non-lockable snippet!")
|
||||
with self.assertRaises(NoReverseMatch):
|
||||
self.client.post(self.get_url("unlock"))
|
||||
|
||||
def test_unlock_post_bad_permissions(self):
|
||||
# Remove privileges from user
|
||||
self.user.is_superuser = False
|
||||
self.user.groups.add(Group.objects.get(name="Editors"))
|
||||
self.user.save()
|
||||
|
||||
# Lock the snippet
|
||||
self.lock_snippet(self.create_user("user2"))
|
||||
|
||||
response = self.client.post(self.get_url("unlock"))
|
||||
self.refresh_snippet()
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
# Check that the snippet is still locked
|
||||
self.assertTrue(self.snippet.locked)
|
||||
self.assertIsNotNone(self.snippet.locked_at)
|
||||
|
||||
def test_unlock_post_own_snippet_with_bad_permissions(self):
|
||||
"""User can unlock a snippet they have locked without the unlock permission."""
|
||||
|
||||
# Remove privileges from user
|
||||
self.user.is_superuser = False
|
||||
self.user.groups.add(Group.objects.get(name="Editors"))
|
||||
self.user.save()
|
||||
|
||||
# Lock the snippet
|
||||
self.lock_snippet(self.user)
|
||||
|
||||
next_url = reverse("wagtailadmin_home")
|
||||
response = self.client.post(self.get_url("unlock"), {"next": next_url})
|
||||
self.refresh_snippet()
|
||||
|
||||
# Check response
|
||||
self.assertRedirects(response, next_url)
|
||||
|
||||
# Check that the snippet is not locked
|
||||
self.assertFalse(self.snippet.locked)
|
||||
self.assertIsNone(self.snippet.locked_by)
|
||||
self.assertIsNone(self.snippet.locked_at)
|
||||
|
||||
|
||||
class TestLockingWithDraftState(DraftStateModelTestCase, TestLocking):
|
||||
pass
|
||||
|
||||
|
||||
class TestEditLockedSnippet(BaseLockingTestCase):
|
||||
save_button_label = "Save"
|
||||
|
||||
def test_edit_post_locked_by_another_user(self):
|
||||
"""A user cannot edit a snippet that is locked by another user."""
|
||||
# Lock the snippet
|
||||
self.lock_snippet(self.create_user("user2"))
|
||||
|
||||
# Try to edit the snippet
|
||||
response = self.client.post(
|
||||
self.get_url("edit"),
|
||||
{"text": "Edited while locked"},
|
||||
)
|
||||
self.refresh_snippet()
|
||||
|
||||
# Should show lock message
|
||||
self.assertContains(
|
||||
response,
|
||||
f"The {self.model_name} could not be saved as it is locked",
|
||||
)
|
||||
|
||||
# Check that the snippet is still locked
|
||||
self.assertTrue(self.snippet.locked)
|
||||
|
||||
# Check that the snippet is not edited
|
||||
self.assertEqual(self.snippet.text, "I'm a lockable snippet!")
|
||||
|
||||
def test_edit_post_locked_by_self(self):
|
||||
"""A user can edit a snippet that is locked by themselves."""
|
||||
# Lock the snippet
|
||||
self.lock_snippet(self.user)
|
||||
|
||||
# Try to edit the snippet
|
||||
response = self.client.post(
|
||||
self.get_url("edit"),
|
||||
{"text": "Edited while locked"},
|
||||
follow=True,
|
||||
)
|
||||
self.refresh_snippet()
|
||||
|
||||
# Should not show error message
|
||||
self.assertNotContains(
|
||||
response,
|
||||
f"The {self.model_name} could not be saved as it is locked",
|
||||
)
|
||||
|
||||
# Check that the snippet is still locked
|
||||
self.assertTrue(self.snippet.locked)
|
||||
|
||||
# Check that the snippet is edited
|
||||
self.assertEqual(self.snippet.text, "Edited while locked")
|
||||
|
||||
@override_settings(WAGTAILADMIN_GLOBAL_EDIT_LOCK=True)
|
||||
def test_edit_post_locked_by_self_with_global_lock_enabled(self):
|
||||
"""A user cannot edit a snippet that is locked by themselves if the setting is enabled."""
|
||||
# Lock the snippet
|
||||
self.lock_snippet(self.user)
|
||||
|
||||
# Try to edit the snippet
|
||||
response = self.client.post(
|
||||
self.get_url("edit"),
|
||||
{"text": "Edited while locked"},
|
||||
)
|
||||
self.refresh_snippet()
|
||||
|
||||
# Should show lock message
|
||||
self.assertContains(
|
||||
response,
|
||||
f"The {self.model_name} could not be saved as it is locked",
|
||||
)
|
||||
|
||||
# Check that the snippet is still locked
|
||||
self.assertTrue(self.snippet.locked)
|
||||
|
||||
# Check that the snippet is not edited
|
||||
self.assertEqual(self.snippet.text, "I'm a lockable snippet!")
|
||||
|
||||
def test_edit_get_locked_by_self(self):
|
||||
"""A user can edit and unlock a snippet that is locked by themselves."""
|
||||
cases = [
|
||||
(["change", "unlock"]),
|
||||
(["change"]), # Can unlock even without unlock permission
|
||||
]
|
||||
|
||||
for permissions in cases:
|
||||
with self.subTest(
|
||||
"User can edit and unlock an object they have locked",
|
||||
permissions=permissions,
|
||||
):
|
||||
# Lock the snippet
|
||||
self.lock_snippet(self.user)
|
||||
|
||||
# Use the specified permissions
|
||||
self.set_permissions(permissions)
|
||||
|
||||
# Get the edit page
|
||||
response = self.client.get(self.get_url("edit"))
|
||||
html = response.content.decode()
|
||||
unlock_url = self.get_url("unlock")
|
||||
|
||||
# Should show lock message
|
||||
self.assertContains(
|
||||
response,
|
||||
"<b>'I'm a lockable snippet!' was locked</b> by <b>you</b> on",
|
||||
)
|
||||
|
||||
# Should show Save action menu item
|
||||
self.assertContains(
|
||||
response,
|
||||
f'<em data-w-progress-target="label">{self.save_button_label}</em>',
|
||||
html=True,
|
||||
)
|
||||
|
||||
# Should not show Locked action menu item
|
||||
self.assertTagInHTML(
|
||||
'<button type="submit" disabled>Locked</button>',
|
||||
html,
|
||||
count=0,
|
||||
allow_extra_attrs=True,
|
||||
)
|
||||
|
||||
# Should show lock information in the side panel
|
||||
self.assertContains(
|
||||
response,
|
||||
f"Only you can make changes while the {self.model_name} is locked",
|
||||
)
|
||||
|
||||
# Should show unlock toggle in the side panel
|
||||
self.assertTagInHTML(
|
||||
f'<input type="checkbox" checked data-action="click->w-action#post" data-controller="w-action" data-w-action-url-value="{unlock_url}">',
|
||||
html,
|
||||
count=1,
|
||||
allow_extra_attrs=True,
|
||||
)
|
||||
# Should show unlock button in the message
|
||||
self.assertTagInHTML(
|
||||
f'<button type="button" data-action="w-action#post" data-controller="w-action" data-w-action-url-value="{unlock_url}">Unlock</button>',
|
||||
html,
|
||||
count=1,
|
||||
allow_extra_attrs=True,
|
||||
)
|
||||
|
||||
def test_edit_get_locked_by_another_user_has_unlock_permission(self):
|
||||
"""A user needs to unlock a snippet that's locked by another user in order to edit it."""
|
||||
user = self.create_user("user2")
|
||||
# Lock the snippet
|
||||
self.lock_snippet(user)
|
||||
|
||||
# Use edit and unlock permissions
|
||||
self.set_permissions(["change", "unlock"])
|
||||
|
||||
# Get the edit page
|
||||
response = self.client.get(self.get_url("edit"))
|
||||
html = response.content.decode()
|
||||
unlock_url = self.get_url("unlock")
|
||||
display_name = get_user_display_name(user)
|
||||
|
||||
# Should show lock message
|
||||
self.assertContains(
|
||||
response,
|
||||
f"<b>'I'm a lockable snippet!' was locked</b> by <b>{user}</b> on",
|
||||
)
|
||||
|
||||
# Should show lock information in the side panel
|
||||
self.assertContains(
|
||||
response,
|
||||
f"Only {display_name} can make changes while the {self.model_name} is locked",
|
||||
)
|
||||
|
||||
# Should not show Save action menu item
|
||||
self.assertNotContains(
|
||||
response,
|
||||
f'<em data-w-progress-target="label">{self.save_button_label}</em>',
|
||||
html=True,
|
||||
)
|
||||
|
||||
# Should show Locked action menu item
|
||||
self.assertTagInHTML(
|
||||
'<button type="submit" disabled>Locked</button>',
|
||||
html,
|
||||
count=1,
|
||||
allow_extra_attrs=True,
|
||||
)
|
||||
|
||||
# Should show unlock toggle in the side panel
|
||||
self.assertTagInHTML(
|
||||
f'<input type="checkbox" checked data-action="click->w-action#post" data-controller="w-action" data-w-action-url-value="{unlock_url}">',
|
||||
html,
|
||||
count=1,
|
||||
allow_extra_attrs=True,
|
||||
)
|
||||
# Should show unlock button in the message
|
||||
self.assertTagInHTML(
|
||||
f'<button type="button" data-action="w-action#post" data-controller="w-action" data-w-action-url-value="{unlock_url}">Unlock</button>',
|
||||
html,
|
||||
count=1,
|
||||
allow_extra_attrs=True,
|
||||
)
|
||||
|
||||
def test_edit_get_locked_by_another_user_no_unlock_permission(self):
|
||||
"""
|
||||
A different user cannot unlock the object without the unlock permission.
|
||||
"""
|
||||
user = self.create_user("user2")
|
||||
# Lock the snippet
|
||||
self.lock_snippet(user)
|
||||
|
||||
# Use edit permission only
|
||||
self.set_permissions(["change"])
|
||||
|
||||
# Get the edit page
|
||||
response = self.client.get(self.get_url("edit"))
|
||||
html = response.content.decode()
|
||||
unlock_url = self.get_url("unlock")
|
||||
display_name = get_user_display_name(user)
|
||||
|
||||
# Should show lock message
|
||||
self.assertContains(
|
||||
response,
|
||||
f"<b>'I'm a lockable snippet!' was locked</b> by <b>{user}</b> on",
|
||||
)
|
||||
|
||||
# Should show lock information in the side panel
|
||||
self.assertContains(
|
||||
response,
|
||||
f"Only {display_name} can make changes while the {self.model_name} is locked",
|
||||
)
|
||||
|
||||
# Should not show instruction to unlock
|
||||
self.assertNotContains(response, "Unlock")
|
||||
|
||||
# Should not show Save action menu item
|
||||
self.assertNotContains(
|
||||
response,
|
||||
f'<em data-w-progress-target="label">{self.save_button_label}</em>',
|
||||
html=True,
|
||||
)
|
||||
|
||||
# Should show Locked action menu item
|
||||
self.assertTagInHTML(
|
||||
'<button type="submit" disabled>Locked</button>',
|
||||
html,
|
||||
count=1,
|
||||
allow_extra_attrs=True,
|
||||
)
|
||||
|
||||
# Should not show unlock toggle in the side panel
|
||||
self.assertTagInHTML(
|
||||
f'<input type="checkbox" checked data-action="click->w-action#post" data-controller="w-action" data-w-action-url-value="{unlock_url}">',
|
||||
html,
|
||||
count=0,
|
||||
allow_extra_attrs=True,
|
||||
)
|
||||
# Should not show unlock button in the message
|
||||
self.assertTagInHTML(
|
||||
f'<button type="button" data-action="w-action#post" data-controller="w-action" data-w-action-url-value="{unlock_url}">Unlock</button>',
|
||||
html,
|
||||
count=0,
|
||||
allow_extra_attrs=True,
|
||||
)
|
||||
|
||||
def test_edit_get_unlocked_no_lock_permission(self):
|
||||
"""A user cannot lock an object without the lock permission."""
|
||||
# Use edit permission only
|
||||
self.set_permissions(["change"])
|
||||
|
||||
# Get the edit page
|
||||
response = self.client.get(self.get_url("edit"))
|
||||
html = response.content.decode()
|
||||
lock_url = self.get_url("lock")
|
||||
|
||||
# Should not show lock message
|
||||
self.assertNotContains(
|
||||
response,
|
||||
"<b>'I'm a lockable snippet!' was locked</b>",
|
||||
)
|
||||
|
||||
# Should show unlocked information in the side panel
|
||||
self.assertContains(
|
||||
response,
|
||||
f"Anyone can edit this {self.model_name}",
|
||||
)
|
||||
|
||||
# Should not show info to lock the object in the side panel
|
||||
self.assertNotContains(
|
||||
response,
|
||||
"lock it to prevent others from editing",
|
||||
)
|
||||
|
||||
# Should show Save action menu item
|
||||
self.assertContains(
|
||||
response,
|
||||
f'<em data-w-progress-target="label">{self.save_button_label}</em>',
|
||||
html=True,
|
||||
)
|
||||
|
||||
# Should not show Locked action menu item
|
||||
self.assertTagInHTML(
|
||||
'<button type="submit" disabled>Locked</button>',
|
||||
html,
|
||||
count=0,
|
||||
allow_extra_attrs=True,
|
||||
)
|
||||
|
||||
# Should not show lock toggle in the side panel
|
||||
self.assertTagInHTML(
|
||||
f'<input type="checkbox" data-action="click->w-action#post" data-controller="w-action" data-w-action-url-value="{lock_url}">',
|
||||
html,
|
||||
count=0,
|
||||
allow_extra_attrs=True,
|
||||
)
|
||||
# Should not show lock button in the message
|
||||
self.assertTagInHTML(
|
||||
f'<button type="button" data-action="w-action#post" data-controller="w-action" data-w-action-url-value="{lock_url}">Lock</button>',
|
||||
html,
|
||||
count=0,
|
||||
allow_extra_attrs=True,
|
||||
)
|
||||
|
||||
def test_edit_get_unlocked_has_lock_permission(self):
|
||||
"""A user can lock an object with the lock permission."""
|
||||
# Use edit and lock permissions
|
||||
self.set_permissions(["change", "lock"])
|
||||
|
||||
# Get the edit page
|
||||
response = self.client.get(self.get_url("edit"))
|
||||
html = response.content.decode()
|
||||
lock_url = self.get_url("lock")
|
||||
|
||||
# Should not show lock message
|
||||
self.assertNotContains(
|
||||
response,
|
||||
"<b>'I'm a lockable snippet!' was locked</b>",
|
||||
)
|
||||
|
||||
# Should show unlocked information in the side panel
|
||||
self.assertContains(
|
||||
response,
|
||||
f"Anyone can edit this {self.model_name} – lock it to prevent others from editing",
|
||||
)
|
||||
|
||||
# Should show Save action menu item
|
||||
self.assertContains(
|
||||
response,
|
||||
f'<em data-w-progress-target="label">{self.save_button_label}</em>',
|
||||
html=True,
|
||||
)
|
||||
|
||||
# Should not show Locked action menu item
|
||||
self.assertTagInHTML(
|
||||
'<button type="submit" disabled>Locked</button>',
|
||||
html,
|
||||
count=0,
|
||||
allow_extra_attrs=True,
|
||||
)
|
||||
|
||||
# Should show lock toggle in the side panel
|
||||
self.assertTagInHTML(
|
||||
f'<input type="checkbox" data-action="click->w-action#post" data-controller="w-action" data-w-action-url-value="{lock_url}">',
|
||||
html,
|
||||
count=1,
|
||||
allow_extra_attrs=True,
|
||||
)
|
||||
|
||||
|
||||
class TestEditLockedDraftStateSnippet(DraftStateModelTestCase, TestEditLockedSnippet):
|
||||
save_button_label = "Save draft"
|
||||
|
||||
|
||||
class TestWorkflowLock(BaseLockingTestCase):
|
||||
model = FullFeaturedSnippet
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.snippet.save_revision()
|
||||
self.moderator = self.create_user("moderator")
|
||||
self.moderators = Group.objects.get(name="Moderators")
|
||||
self.moderator.groups.add(self.moderators)
|
||||
self.set_permissions(["change"], user=self.user)
|
||||
self.set_permissions(["change", "publish"], user=self.moderator)
|
||||
|
||||
def test_when_locked_by_workflow(self):
|
||||
workflow = Workflow.objects.create(name="test_workflow")
|
||||
task = GroupApprovalTask.objects.create(name="test_task")
|
||||
task.groups.add(self.moderators)
|
||||
WorkflowTask.objects.create(workflow=workflow, task=task, sort_order=1)
|
||||
workflow.start(self.snippet, self.user)
|
||||
|
||||
lock = self.snippet.get_lock()
|
||||
self.assertIsInstance(lock, WorkflowLock)
|
||||
self.assertTrue(lock.for_user(self.user))
|
||||
self.assertFalse(lock.for_user(self.moderator))
|
||||
self.assertEqual(
|
||||
lock.get_message(self.user),
|
||||
"This full-featured snippet is currently awaiting moderation. "
|
||||
"Only reviewers for this task can edit the full-featured snippet.",
|
||||
)
|
||||
self.assertIsNone(lock.get_message(self.moderator))
|
||||
|
||||
# When visiting a snippet in a workflow with multiple tasks, the message
|
||||
# displayed to users changes to show the current task the snippet is on
|
||||
|
||||
# Add a second task to the workflow
|
||||
other_task = GroupApprovalTask.objects.create(name="another_task")
|
||||
WorkflowTask.objects.create(workflow=workflow, task=other_task, sort_order=2)
|
||||
|
||||
lock = self.snippet.get_lock()
|
||||
self.assertEqual(
|
||||
lock.get_message(self.user),
|
||||
"This full-featured snippet is awaiting <b>'test_task'</b> in the "
|
||||
"<b>'test_workflow'</b> workflow. Only reviewers for this task "
|
||||
"can edit the full-featured snippet.",
|
||||
)
|
||||
26
env/lib/python3.10/site-packages/wagtail/snippets/tests/test_management.py
vendored
Normal file
26
env/lib/python3.10/site-packages/wagtail/snippets/tests/test_management.py
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
from django.apps import apps
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import migrations
|
||||
from django.test import TestCase
|
||||
|
||||
from wagtail.snippets.models import create_extra_permissions
|
||||
|
||||
|
||||
class TestCreatePermissions(TestCase):
|
||||
def setUp(self):
|
||||
self.app_config = apps.get_app_config("auth")
|
||||
|
||||
def tearDown(self):
|
||||
ContentType.objects.clear_cache()
|
||||
|
||||
def test_unavailable_models(self):
|
||||
state = migrations.state.ProjectState()
|
||||
|
||||
# Unavailable contenttypes.ContentType
|
||||
with self.assertNumQueries(0):
|
||||
create_extra_permissions(self.app_config, verbosity=0, apps=state.apps)
|
||||
|
||||
# Unavailable auth.Permission
|
||||
state = migrations.state.ProjectState(real_apps={"contenttypes"})
|
||||
with self.assertNumQueries(0):
|
||||
create_extra_permissions(self.app_config, verbosity=0, apps=state.apps)
|
||||
509
env/lib/python3.10/site-packages/wagtail/snippets/tests/test_preview.py
vendored
Normal file
509
env/lib/python3.10/site-packages/wagtail/snippets/tests/test_preview.py
vendored
Normal file
@@ -0,0 +1,509 @@
|
||||
import datetime
|
||||
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from freezegun import freeze_time
|
||||
|
||||
from wagtail.admin.views.generic.preview import PreviewOnEdit
|
||||
from wagtail.test.testapp.models import (
|
||||
EventCategory,
|
||||
MultiPreviewModesModel,
|
||||
NonPreviewableModel,
|
||||
PreviewableModel,
|
||||
RevisableModel,
|
||||
)
|
||||
from wagtail.test.utils import WagtailTestUtils
|
||||
|
||||
|
||||
class TestPreview(WagtailTestUtils, TestCase):
|
||||
def setUp(self):
|
||||
self.user = self.login()
|
||||
|
||||
self.meetings_category = EventCategory.objects.create(name="Meetings")
|
||||
self.parties_category = EventCategory.objects.create(name="Parties")
|
||||
self.holidays_category = EventCategory.objects.create(name="Holidays")
|
||||
self.snippet = PreviewableModel.objects.create(text="A previewable snippet")
|
||||
|
||||
self.preview_on_add_url = reverse(
|
||||
"wagtailsnippets_tests_previewablemodel:preview_on_add"
|
||||
)
|
||||
self.preview_on_edit_url = reverse(
|
||||
"wagtailsnippets_tests_previewablemodel:preview_on_edit",
|
||||
args=(self.snippet.pk,),
|
||||
)
|
||||
self.session_key_prefix = "wagtail-preview-tests-previewablemodel"
|
||||
self.edit_session_key = f"{self.session_key_prefix}-{self.snippet.pk}"
|
||||
|
||||
self.post_data = {
|
||||
"text": "An edited previewable snippet",
|
||||
"categories": [self.parties_category.id, self.holidays_category.id],
|
||||
}
|
||||
|
||||
def test_preview_on_create_with_no_session_data(self):
|
||||
self.assertNotIn(self.session_key_prefix, self.client.session)
|
||||
|
||||
response = self.client.get(self.preview_on_add_url)
|
||||
|
||||
# The preview should be unavailable
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, "wagtailadmin/generic/preview_error.html")
|
||||
self.assertContains(
|
||||
response,
|
||||
"<title>Preview not available - Wagtail</title>",
|
||||
html=True,
|
||||
)
|
||||
self.assertContains(
|
||||
response,
|
||||
'<h1 class="preview-error__title">Preview not available</h1>',
|
||||
html=True,
|
||||
)
|
||||
|
||||
def test_preview_on_create_with_invalid_data(self):
|
||||
self.assertNotIn(self.session_key_prefix, self.client.session)
|
||||
|
||||
response = self.client.post(self.preview_on_add_url, {"text": ""})
|
||||
|
||||
# Check the JSON response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertJSONEqual(
|
||||
response.content.decode(),
|
||||
{"is_valid": False, "is_available": False},
|
||||
)
|
||||
|
||||
# The invalid data should not be saved in the session
|
||||
self.assertNotIn(self.session_key_prefix, self.client.session)
|
||||
|
||||
response = self.client.get(self.preview_on_add_url)
|
||||
|
||||
# The preview should still be unavailable
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, "wagtailadmin/generic/preview_error.html")
|
||||
self.assertContains(
|
||||
response,
|
||||
"<title>Preview not available - Wagtail</title>",
|
||||
html=True,
|
||||
)
|
||||
self.assertContains(
|
||||
response,
|
||||
'<h1 class="preview-error__title">Preview not available</h1>',
|
||||
html=True,
|
||||
)
|
||||
|
||||
def test_preview_on_create_with_m2m_field(self):
|
||||
response = self.client.post(self.preview_on_add_url, self.post_data)
|
||||
|
||||
# Check the JSON response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertJSONEqual(
|
||||
response.content.decode(),
|
||||
{"is_valid": True, "is_available": True},
|
||||
)
|
||||
|
||||
# Check the user can refresh the preview
|
||||
self.assertIn(self.session_key_prefix, self.client.session)
|
||||
|
||||
response = self.client.get(self.preview_on_add_url)
|
||||
|
||||
# Check the HTML response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, "tests/previewable_model.html")
|
||||
self.assertContains(response, "An edited previewable snippet")
|
||||
self.assertContains(response, "<li>Parties</li>")
|
||||
self.assertContains(response, "<li>Holidays</li>")
|
||||
|
||||
def test_preview_on_edit_with_m2m_field(self):
|
||||
response = self.client.post(self.preview_on_edit_url, self.post_data)
|
||||
|
||||
# Check the JSON response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertJSONEqual(
|
||||
response.content.decode(),
|
||||
{"is_valid": True, "is_available": True},
|
||||
)
|
||||
|
||||
# Check the user can refresh the preview
|
||||
self.assertIn(self.edit_session_key, self.client.session)
|
||||
|
||||
response = self.client.get(self.preview_on_edit_url)
|
||||
|
||||
# Check the HTML response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, "tests/previewable_model.html")
|
||||
self.assertContains(response, "An edited previewable snippet")
|
||||
self.assertContains(response, "<li>Parties</li>")
|
||||
self.assertContains(response, "<li>Holidays</li>")
|
||||
|
||||
def test_preview_on_edit_with_valid_then_invalid_data(self):
|
||||
response = self.client.post(self.preview_on_edit_url, self.post_data)
|
||||
|
||||
# Check the JSON response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertJSONEqual(
|
||||
response.content.decode(),
|
||||
{"is_valid": True, "is_available": True},
|
||||
)
|
||||
|
||||
# Send an invalid update request
|
||||
response = self.client.post(
|
||||
self.preview_on_edit_url, {**self.post_data, "text": ""}
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertJSONEqual(
|
||||
response.content.decode(),
|
||||
{"is_valid": False, "is_available": True},
|
||||
)
|
||||
|
||||
# Check the user can still see the preview with the last valid data
|
||||
self.assertIn(self.edit_session_key, self.client.session)
|
||||
|
||||
response = self.client.get(self.preview_on_edit_url)
|
||||
|
||||
# Check the HTML response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, "tests/previewable_model.html")
|
||||
self.assertContains(response, "An edited previewable snippet")
|
||||
self.assertContains(response, "<li>Parties</li>")
|
||||
self.assertContains(response, "<li>Holidays</li>")
|
||||
|
||||
def test_preview_on_edit_expiry(self):
|
||||
initial_datetime = timezone.now()
|
||||
expiry_datetime = initial_datetime + datetime.timedelta(
|
||||
seconds=PreviewOnEdit.preview_expiration_timeout + 1
|
||||
)
|
||||
|
||||
new_snippet = PreviewableModel.objects.create(text="A new previewable snippet")
|
||||
|
||||
with freeze_time(initial_datetime) as frozen_datetime:
|
||||
response = self.client.post(self.preview_on_edit_url, self.post_data)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response = self.client.get(self.preview_on_edit_url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
frozen_datetime.move_to(expiry_datetime)
|
||||
|
||||
preview_url = reverse(
|
||||
"wagtailsnippets_tests_previewablemodel:preview_on_edit",
|
||||
args=(new_snippet.pk,),
|
||||
)
|
||||
|
||||
response = self.client.post(preview_url, self.post_data)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response = self.client.get(preview_url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Stale preview data should be removed from the session
|
||||
self.assertNotIn(self.edit_session_key, self.client.session)
|
||||
self.assertIn(
|
||||
f"{self.session_key_prefix}-{new_snippet.pk}",
|
||||
self.client.session,
|
||||
)
|
||||
|
||||
def test_preview_on_create_clear_preview_data(self):
|
||||
# Set a fake preview session data for the page
|
||||
self.client.session[self.session_key_prefix] = "test data"
|
||||
|
||||
response = self.client.delete(self.preview_on_add_url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertJSONEqual(
|
||||
response.content.decode(),
|
||||
{"success": True},
|
||||
)
|
||||
|
||||
# The data should no longer exist in the session
|
||||
self.assertNotIn(self.session_key_prefix, self.client.session)
|
||||
|
||||
response = self.client.get(self.preview_on_add_url)
|
||||
|
||||
# The preview should be unavailable
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, "wagtailadmin/generic/preview_error.html")
|
||||
self.assertContains(
|
||||
response,
|
||||
"<title>Preview not available - Wagtail</title>",
|
||||
html=True,
|
||||
)
|
||||
self.assertContains(
|
||||
response,
|
||||
'<h1 class="preview-error__title">Preview not available</h1>',
|
||||
html=True,
|
||||
)
|
||||
|
||||
def test_preview_on_edit_clear_preview_data(self):
|
||||
# Set a fake preview session data for the page
|
||||
self.client.session[self.edit_session_key] = "test data"
|
||||
|
||||
response = self.client.delete(self.preview_on_edit_url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertJSONEqual(
|
||||
response.content.decode(),
|
||||
{"success": True},
|
||||
)
|
||||
|
||||
# The data should no longer exist in the session
|
||||
self.assertNotIn(self.edit_session_key, self.client.session)
|
||||
|
||||
response = self.client.get(self.preview_on_edit_url)
|
||||
|
||||
# The preview should be unavailable
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, "wagtailadmin/generic/preview_error.html")
|
||||
self.assertContains(
|
||||
response,
|
||||
"<title>Preview not available - Wagtail</title>",
|
||||
html=True,
|
||||
)
|
||||
self.assertContains(
|
||||
response,
|
||||
'<h1 class="preview-error__title">Preview not available</h1>',
|
||||
html=True,
|
||||
)
|
||||
|
||||
def test_preview_revision(self):
|
||||
snippet = MultiPreviewModesModel.objects.create(text="Multiple modes")
|
||||
revision = snippet.save_revision(log_action=True)
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wagtailsnippets_tests_multipreviewmodesmodel:revisions_view",
|
||||
args=(snippet.pk, revision.id),
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Should respect the default_preview_mode
|
||||
self.assertTemplateUsed(response, "tests/previewable_model_alt.html")
|
||||
self.assertContains(response, "Multiple modes (Alternate Preview)")
|
||||
|
||||
|
||||
class TestEnablePreview(WagtailTestUtils, TestCase):
|
||||
def setUp(self):
|
||||
self.user = self.login()
|
||||
self.single = PreviewableModel.objects.create(text="Single preview mode")
|
||||
self.multiple = MultiPreviewModesModel.objects.create(
|
||||
text="Multiple preview modes"
|
||||
)
|
||||
|
||||
def get_url(self, snippet, name, args=None):
|
||||
model_name = type(snippet)._meta.model_name
|
||||
return reverse(f"wagtailsnippets_tests_{model_name}:{name}", args=args)
|
||||
|
||||
def test_show_preview_panel_on_create_with_single_mode(self):
|
||||
create_url = self.get_url(self.single, "add")
|
||||
preview_url = self.get_url(self.single, "preview_on_add")
|
||||
new_tab_url = preview_url + "?mode="
|
||||
response = self.client.get(create_url)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Should show the preview panel
|
||||
self.assertContains(response, 'data-side-panel="preview"')
|
||||
self.assertContains(response, 'data-action="%s"' % preview_url)
|
||||
|
||||
# Should have the preview side panel toggle button
|
||||
soup = self.get_soup(response.content)
|
||||
toggle_button = soup.find("button", {"data-side-panel-toggle": "preview"})
|
||||
self.assertIsNotNone(toggle_button)
|
||||
self.assertEqual("w-tooltip w-kbd", toggle_button["data-controller"])
|
||||
self.assertEqual("mod+p", toggle_button["data-w-kbd-key-value"])
|
||||
|
||||
# Should show the iframe
|
||||
self.assertContains(
|
||||
response,
|
||||
'<iframe id="preview-iframe" loading="lazy" title="Preview" class="preview-panel__iframe" data-preview-iframe aria-describedby="preview-panel-error-banner">',
|
||||
)
|
||||
|
||||
# Should show the new tab button with the default mode set
|
||||
self.assertContains(response, f'href="{new_tab_url}" target="_blank"')
|
||||
|
||||
# Should not show the preview mode selection
|
||||
self.assertNotContains(
|
||||
response,
|
||||
'<select id="id_preview_mode" name="preview_mode" class="preview-panel__mode-select" data-preview-mode-select>',
|
||||
)
|
||||
|
||||
def test_show_preview_panel_on_create_with_multiple_modes(self):
|
||||
create_url = self.get_url(self.multiple, "add")
|
||||
preview_url = self.get_url(self.multiple, "preview_on_add")
|
||||
new_tab_url = preview_url + "?mode=alt%231"
|
||||
response = self.client.get(create_url)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Should show the preview panel
|
||||
self.assertContains(response, 'data-side-panel-toggle="preview"')
|
||||
self.assertContains(response, 'data-side-panel="preview"')
|
||||
self.assertContains(response, 'data-action="%s"' % preview_url)
|
||||
|
||||
# Should show the iframe
|
||||
self.assertContains(
|
||||
response,
|
||||
'<iframe id="preview-iframe" loading="lazy" title="Preview" class="preview-panel__iframe" data-preview-iframe aria-describedby="preview-panel-error-banner">',
|
||||
)
|
||||
|
||||
# Should show the new tab button with the default mode set and correctly quoted
|
||||
self.assertContains(response, f'href="{new_tab_url}" target="_blank"')
|
||||
|
||||
# should show the preview mode selection
|
||||
self.assertContains(
|
||||
response,
|
||||
'<select id="id_preview_mode" name="preview_mode" class="preview-panel__mode-select" data-preview-mode-select>',
|
||||
)
|
||||
self.assertContains(response, '<option value="">Normal</option>')
|
||||
|
||||
# Should respect the default_preview_mode
|
||||
self.assertContains(
|
||||
response, '<option value="alt#1" selected>Alternate</option>'
|
||||
)
|
||||
|
||||
def test_show_preview_panel_on_edit_with_single_mode(self):
|
||||
edit_url = self.get_url(self.single, "edit", args=(self.single.pk,))
|
||||
preview_url = self.get_url(
|
||||
self.single, "preview_on_edit", args=(self.multiple.pk,)
|
||||
)
|
||||
new_tab_url = preview_url + "?mode="
|
||||
response = self.client.get(edit_url)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Should show the preview panel
|
||||
self.assertContains(response, 'data-side-panel-toggle="preview"')
|
||||
self.assertContains(response, 'data-side-panel="preview"')
|
||||
self.assertContains(response, 'data-action="%s"' % preview_url)
|
||||
|
||||
# Should show the iframe
|
||||
self.assertContains(
|
||||
response,
|
||||
'<iframe id="preview-iframe" loading="lazy" title="Preview" class="preview-panel__iframe" data-preview-iframe aria-describedby="preview-panel-error-banner">',
|
||||
)
|
||||
|
||||
# Should show the new tab button with the default mode set
|
||||
self.assertContains(response, f'href="{new_tab_url}" target="_blank"')
|
||||
|
||||
# Should not show the preview mode selection
|
||||
self.assertNotContains(
|
||||
response,
|
||||
'<select id="id_preview_mode" name="preview_mode" class="preview-panel__mode-select" data-preview-mode-select>',
|
||||
)
|
||||
|
||||
def test_show_preview_panel_on_edit_with_multiple_modes(self):
|
||||
edit_url = self.get_url(self.multiple, "edit", args=(self.multiple.pk,))
|
||||
preview_url = self.get_url(
|
||||
self.multiple, "preview_on_edit", args=(self.multiple.pk,)
|
||||
)
|
||||
new_tab_url = preview_url + "?mode=alt%231"
|
||||
response = self.client.get(edit_url)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Should show the preview panel
|
||||
self.assertContains(response, 'data-side-panel-toggle="preview"')
|
||||
self.assertContains(response, 'data-side-panel="preview"')
|
||||
self.assertContains(response, 'data-action="%s"' % preview_url)
|
||||
|
||||
# Should show the iframe
|
||||
self.assertContains(
|
||||
response,
|
||||
'<iframe id="preview-iframe" loading="lazy" title="Preview" class="preview-panel__iframe" data-preview-iframe aria-describedby="preview-panel-error-banner">',
|
||||
)
|
||||
|
||||
# Should show the new tab button with the default mode set and correctly quoted
|
||||
self.assertContains(response, f'href="{new_tab_url}" target="_blank"')
|
||||
|
||||
# should show the preview mode selection
|
||||
self.assertContains(
|
||||
response,
|
||||
'<select id="id_preview_mode" name="preview_mode" class="preview-panel__mode-select" data-preview-mode-select>',
|
||||
)
|
||||
self.assertContains(response, '<option value="">Normal</option>')
|
||||
|
||||
# Should respect the default_preview_mode
|
||||
self.assertContains(
|
||||
response, '<option value="alt#1" selected>Alternate</option>'
|
||||
)
|
||||
|
||||
def test_show_preview_on_revisions_list(self):
|
||||
latest_revision = self.multiple.save_revision(log_action=True)
|
||||
history_url = self.get_url(self.multiple, "history", args=(self.multiple.pk,))
|
||||
preview_url = self.get_url(
|
||||
self.multiple,
|
||||
"revisions_view",
|
||||
args=(self.multiple.pk, latest_revision.id),
|
||||
)
|
||||
|
||||
response = self.client.get(history_url)
|
||||
self.assertContains(response, "Preview")
|
||||
self.assertContains(response, preview_url)
|
||||
|
||||
|
||||
class TestDisablePreviewWithEmptyModes(WagtailTestUtils, TestCase):
|
||||
"""
|
||||
Preview can be disabled by setting preview_modes to an empty list.
|
||||
"""
|
||||
|
||||
# NonPreviewableModel has preview_modes = []
|
||||
model = NonPreviewableModel
|
||||
|
||||
def setUp(self):
|
||||
self.user = self.login()
|
||||
self.snippet = self.model.objects.create(text="A non-previewable snippet")
|
||||
self.model_name = self.model._meta.model_name
|
||||
|
||||
def get_url(self, name, args=None):
|
||||
return reverse(f"wagtailsnippets_tests_{self.model_name}:{name}", args=args)
|
||||
|
||||
def test_disable_preview_on_create(self):
|
||||
response = self.client.get(self.get_url("add"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
preview_url = self.get_url("preview_on_add")
|
||||
self.assertNotContains(response, 'data-side-panel-toggle="preview"')
|
||||
self.assertNotContains(response, 'data-side-panel="preview"')
|
||||
self.assertNotContains(response, 'data-action="%s"' % preview_url)
|
||||
|
||||
def test_disable_preview_on_edit(self):
|
||||
response = self.client.get(self.get_url("edit", args=(self.snippet.pk,)))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
preview_url = self.get_url("preview_on_edit", args=(self.snippet.pk,))
|
||||
self.assertNotContains(response, 'data-side-panel-toggle="preview"')
|
||||
self.assertNotContains(response, 'data-side-panel="preview"')
|
||||
self.assertNotContains(response, 'data-action="%s"' % preview_url)
|
||||
|
||||
def test_disable_preview_on_revisions_list(self):
|
||||
latest_revision = self.snippet.save_revision(log_action=True)
|
||||
|
||||
response = self.client.get(self.get_url("history", args=(self.snippet.pk,)))
|
||||
preview_url = self.get_url(
|
||||
"revisions_view", args=(self.snippet.pk, latest_revision.id)
|
||||
)
|
||||
|
||||
self.assertNotContains(response, preview_url)
|
||||
|
||||
soup = self.get_soup(response.content)
|
||||
|
||||
preview_link = soup.find("a", {"href": preview_url})
|
||||
self.assertIsNone(preview_link)
|
||||
|
||||
|
||||
class TestDisablePreviewWithoutMixin(TestDisablePreviewWithEmptyModes):
|
||||
"""
|
||||
Preview can be disabled by not extending PreviewableMixin.
|
||||
"""
|
||||
|
||||
# RevisableModel does not extend PreviewableMixin
|
||||
model = RevisableModel
|
||||
|
||||
def get_url(self, name, args=None):
|
||||
# Cannot use reverse() as the urls are not registered
|
||||
# if the model does not extend PreviewableMixin
|
||||
if name == "preview_on_add":
|
||||
return f"/admin/snippets/tests/{self.model_name}/preview/"
|
||||
if name == "preview_on_edit":
|
||||
return f"/admin/snippets/tests/{self.model_name}/preview/{args[0]}/"
|
||||
if name == "revisions_view":
|
||||
return (
|
||||
f"/admin/snippets/tests/{self.model_name}/history/"
|
||||
f"{args[0]}/revisions/{args[1]}/view/"
|
||||
)
|
||||
return super().get_url(name, args)
|
||||
5807
env/lib/python3.10/site-packages/wagtail/snippets/tests/test_snippets.py
vendored
Normal file
5807
env/lib/python3.10/site-packages/wagtail/snippets/tests/test_snippets.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
209
env/lib/python3.10/site-packages/wagtail/snippets/tests/test_usage.py
vendored
Normal file
209
env/lib/python3.10/site-packages/wagtail/snippets/tests/test_usage.py
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
from io import StringIO
|
||||
|
||||
from django.contrib.admin.utils import quote
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.core import management
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from wagtail.models import Page, ReferenceIndex
|
||||
from wagtail.test.testapp.models import (
|
||||
Advert,
|
||||
DraftStateModel,
|
||||
EventPage,
|
||||
GenericSnippetPage,
|
||||
)
|
||||
from wagtail.test.utils import WagtailTestUtils
|
||||
|
||||
|
||||
class TestUsageCount(TestCase):
|
||||
fixtures = ["test.json"]
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
super().setUpTestData()
|
||||
output = StringIO()
|
||||
management.call_command("rebuild_references_index", stdout=output)
|
||||
|
||||
def test_snippet_usage_count(self):
|
||||
advert = Advert.objects.get(pk=1)
|
||||
self.assertEqual(ReferenceIndex.get_grouped_references_to(advert).count(), 2)
|
||||
|
||||
|
||||
class TestUsedBy(TestCase):
|
||||
fixtures = ["test.json"]
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
super().setUpTestData()
|
||||
output = StringIO()
|
||||
management.call_command("rebuild_references_index", stdout=output)
|
||||
|
||||
def test_snippet_used_by(self):
|
||||
advert = Advert.objects.get(pk=1)
|
||||
usage = ReferenceIndex.get_grouped_references_to(advert)
|
||||
|
||||
self.assertIsInstance(usage[0], tuple)
|
||||
self.assertIsInstance(usage[0][0], Page)
|
||||
self.assertIsInstance(usage[0][1], list)
|
||||
self.assertIsInstance(usage[0][1][0], ReferenceIndex)
|
||||
|
||||
|
||||
class TestSnippetUsageView(WagtailTestUtils, TestCase):
|
||||
fixtures = ["test.json"]
|
||||
|
||||
def setUp(self):
|
||||
self.user = self.login()
|
||||
|
||||
def test_use_latest_draft_as_title(self):
|
||||
snippet = DraftStateModel.objects.create(text="Draft-enabled Foo, Published")
|
||||
snippet.save_revision().publish()
|
||||
snippet.text = "Draft-enabled Bar, In Draft"
|
||||
snippet.save_revision()
|
||||
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wagtailsnippets_tests_draftstatemodel:usage",
|
||||
args=[quote(snippet.pk)],
|
||||
)
|
||||
)
|
||||
|
||||
soup = self.get_soup(response.content)
|
||||
sublabel = soup.select_one(".w-breadcrumbs__sublabel")
|
||||
# Should use the latest draft title in the breadcrumbs sublabel
|
||||
self.assertEqual(sublabel.get_text(strip=True), "Draft-enabled Bar, In Draft")
|
||||
|
||||
def test_usage(self):
|
||||
# resave so that usage count gets updated
|
||||
page = Page.objects.get(pk=2)
|
||||
page.save()
|
||||
|
||||
gfk_page = GenericSnippetPage(
|
||||
title="Foobar Title",
|
||||
snippet_content_object=Advert.objects.get(pk=1),
|
||||
)
|
||||
page.add_child(instance=gfk_page)
|
||||
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wagtailsnippets_tests_advert:usage",
|
||||
args=["1"],
|
||||
)
|
||||
)
|
||||
self.assertContains(response, "Welcome to the Wagtail test site!")
|
||||
self.assertContains(response, "Foobar Title")
|
||||
self.assertContains(response, "<td>Generic snippet page</td>", html=True)
|
||||
self.assertContains(response, "Snippet content object")
|
||||
self.assertContains(response, "<th>Field</th>", html=True)
|
||||
self.assertNotContains(response, "<th>If you confirm deletion</th>", html=True)
|
||||
self.assertContains(response, "Snippet content object")
|
||||
|
||||
def test_usage_without_edit_permission_on_snippet(self):
|
||||
# Create a user with basic admin backend access
|
||||
user = self.create_user(
|
||||
username="basicadmin", email="basicadmin@example.com", password="password"
|
||||
)
|
||||
admin_permission = Permission.objects.get(
|
||||
content_type__app_label="wagtailadmin", codename="access_admin"
|
||||
)
|
||||
user.user_permissions.add(admin_permission)
|
||||
self.login(username="basicadmin", password="password")
|
||||
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wagtailsnippets_tests_advert:usage",
|
||||
args=["1"],
|
||||
)
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertRedirects(response, reverse("wagtailadmin_home"))
|
||||
|
||||
def test_usage_without_edit_permission_on_page(self):
|
||||
# resave so that usage count gets updated
|
||||
page = Page.objects.get(pk=2)
|
||||
page.save()
|
||||
|
||||
# Create a user with edit access to snippets but not pages
|
||||
user = self.create_user(
|
||||
username="basicadmin", email="basicadmin@example.com", password="password"
|
||||
)
|
||||
admin_permission = Permission.objects.get(
|
||||
content_type__app_label="wagtailadmin", codename="access_admin"
|
||||
)
|
||||
advert_permission = Permission.objects.get(
|
||||
content_type__app_label="tests", codename="change_advert"
|
||||
)
|
||||
user.user_permissions.add(admin_permission)
|
||||
user.user_permissions.add(advert_permission)
|
||||
self.login(username="basicadmin", password="password")
|
||||
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"wagtailsnippets_tests_advert:usage",
|
||||
args=["1"],
|
||||
)
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertNotContains(response, "Welcome to the Wagtail test site!")
|
||||
self.assertContains(response, "(Private page)")
|
||||
self.assertContains(response, "<td>Page</td>", html=True)
|
||||
self.assertContains(response, "<th>Field</th>", html=True)
|
||||
self.assertNotContains(response, "<th>If you confirm deletion</th>", html=True)
|
||||
self.assertContains(response, "<li>Advert</li>", html=True)
|
||||
|
||||
def test_usage_with_describe_on_delete_cascade(self):
|
||||
# resave so that usage count gets updated
|
||||
page = Page.objects.get(pk=2)
|
||||
page.save()
|
||||
|
||||
response = self.client.get(
|
||||
reverse("wagtailsnippets_tests_advert:usage", args=["1"])
|
||||
+ "?describe_on_delete=1"
|
||||
)
|
||||
self.assertContains(response, "Welcome to the Wagtail test site!")
|
||||
self.assertContains(response, "<td>Page</td>", html=True)
|
||||
self.assertNotContains(response, "<th>Field</th>", html=True)
|
||||
self.assertContains(response, "<th>If you confirm deletion</th>", html=True)
|
||||
self.assertContains(response, "Advert")
|
||||
self.assertContains(response, ": the advert placement will also be deleted")
|
||||
|
||||
def test_usage_with_describe_on_delete_set_null(self):
|
||||
# resave so that usage count gets updated
|
||||
page = EventPage.objects.first()
|
||||
page.save()
|
||||
|
||||
self.assertEqual(page.feed_image.get_usage().count(), 1)
|
||||
|
||||
response = self.client.get(
|
||||
reverse("wagtailimages:image_usage", args=[page.feed_image.id])
|
||||
+ "?describe_on_delete=1"
|
||||
)
|
||||
self.assertContains(response, page.title)
|
||||
self.assertContains(response, "<td>Event page</td>", html=True)
|
||||
self.assertNotContains(response, "<th>Field</th>", html=True)
|
||||
self.assertContains(response, "<th>If you confirm deletion</th>", html=True)
|
||||
self.assertContains(response, "Feed image")
|
||||
self.assertContains(response, ": will unset the reference")
|
||||
|
||||
def test_usage_with_describe_on_delete_gfk(self):
|
||||
advert = Advert.objects.get(pk=1)
|
||||
|
||||
gfk_page = GenericSnippetPage(
|
||||
title="Foobar Title",
|
||||
snippet_content_object=advert,
|
||||
)
|
||||
Page.objects.get(pk=1).add_child(instance=gfk_page)
|
||||
|
||||
self.assertEqual(ReferenceIndex.get_grouped_references_to(advert).count(), 1)
|
||||
|
||||
response = self.client.get(
|
||||
reverse("wagtailsnippets_tests_advert:usage", args=["1"])
|
||||
+ "?describe_on_delete=1"
|
||||
)
|
||||
self.assertNotContains(response, "Welcome to the Wagtail test site!")
|
||||
self.assertContains(response, "Foobar Title")
|
||||
self.assertContains(response, "<td>Generic snippet page</td>", html=True)
|
||||
self.assertNotContains(response, "<th>Field</th>", html=True)
|
||||
self.assertContains(response, "<th>If you confirm deletion</th>", html=True)
|
||||
self.assertContains(response, "Snippet content object")
|
||||
self.assertContains(response, ": will unset the reference")
|
||||
1631
env/lib/python3.10/site-packages/wagtail/snippets/tests/test_viewset.py
vendored
Normal file
1631
env/lib/python3.10/site-packages/wagtail/snippets/tests/test_viewset.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
316
env/lib/python3.10/site-packages/wagtail/snippets/tests/test_workflows.py
vendored
Normal file
316
env/lib/python3.10/site-packages/wagtail/snippets/tests/test_workflows.py
vendored
Normal file
@@ -0,0 +1,316 @@
|
||||
from django.contrib.admin.utils import quote
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase, override_settings
|
||||
from django.urls import reverse
|
||||
|
||||
from wagtail.models import Workflow, WorkflowContentType, WorkflowState
|
||||
from wagtail.test.testapp.models import FullFeaturedSnippet, ModeratedModel
|
||||
from wagtail.test.utils import WagtailTestUtils
|
||||
|
||||
# This module serves to gather snippets-equivalent of workflows-related tests
|
||||
# that are found throughout page-specific test modules, e.g. test_create_page.py,
|
||||
# test_edit_page.py, etc.
|
||||
# The main workflows test modules contain tests for both pages and snippets
|
||||
# and can be found in:
|
||||
# - wagtail.tests.test_workflow
|
||||
# for testing workflow operations through the Workflow model methods
|
||||
# - wagtail.admin.tests.test_workflows
|
||||
# for testing workflow operations through views and testing Workflow settings views
|
||||
|
||||
|
||||
class BaseWorkflowsTestCase(WagtailTestUtils, TestCase):
|
||||
model = FullFeaturedSnippet
|
||||
|
||||
def setUp(self):
|
||||
self.user = self.login()
|
||||
self.object = self.model.objects.create(text="I'm a full-featured snippet!")
|
||||
self.object.save_revision().publish()
|
||||
|
||||
# Assign default workflow to the snippet model
|
||||
self.content_type = ContentType.objects.get_for_model(self.model)
|
||||
self.workflow = Workflow.objects.first()
|
||||
WorkflowContentType.objects.create(
|
||||
content_type=self.content_type,
|
||||
workflow=self.workflow,
|
||||
)
|
||||
|
||||
@property
|
||||
def model_name(self):
|
||||
return self.model._meta.verbose_name
|
||||
|
||||
def get_url(self, name, args=None):
|
||||
args = args if args is not None else [quote(self.object.pk)]
|
||||
return reverse(self.object.snippet_viewset.get_url_name(name), args=args)
|
||||
|
||||
|
||||
class TestCreateView(BaseWorkflowsTestCase):
|
||||
def get(self):
|
||||
return self.client.get(self.get_url("add", ()))
|
||||
|
||||
def post(self, post_data):
|
||||
return self.client.post(self.get_url("add", ()), post_data)
|
||||
|
||||
def test_get_workflow_buttons_shown(self):
|
||||
response = self.get()
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(
|
||||
response,
|
||||
'<button type="submit" name="action-submit" value="Submit to Moderators approval" class="button">',
|
||||
count=1,
|
||||
)
|
||||
|
||||
@override_settings(WAGTAIL_WORKFLOW_ENABLED=False)
|
||||
def test_get_workflow_buttons_not_shown_when_workflow_disabled(self):
|
||||
response = self.get()
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertNotContains(response, 'name="action-submit"')
|
||||
|
||||
def test_post_submit_for_moderation(self):
|
||||
response = self.post({"text": "Newly created", "action-submit": "Submit"})
|
||||
object = self.model.objects.get(text="Newly created")
|
||||
|
||||
self.assertRedirects(response, self.get_url("list", ()))
|
||||
self.assertIsInstance(object, self.model)
|
||||
|
||||
# The object should be created, but not live
|
||||
self.assertEqual(object.text, "Newly created")
|
||||
self.assertFalse(object.live)
|
||||
self.assertFalse(object.first_published_at)
|
||||
|
||||
# The object should now be in moderation
|
||||
self.assertEqual(
|
||||
object.current_workflow_state.status,
|
||||
WorkflowState.STATUS_IN_PROGRESS,
|
||||
)
|
||||
|
||||
# There should be a draft revision with the data
|
||||
self.assertEqual(object.latest_revision.object_str, "Newly created")
|
||||
|
||||
# The current task state should point to the latest revision
|
||||
self.assertEqual(
|
||||
object.current_workflow_task_state.revision,
|
||||
object.latest_revision,
|
||||
)
|
||||
|
||||
|
||||
class TestCreateViewNotLockable(TestCreateView):
|
||||
model = ModeratedModel
|
||||
|
||||
|
||||
class TestEditView(BaseWorkflowsTestCase):
|
||||
def get(self):
|
||||
return self.client.get(self.get_url("edit"))
|
||||
|
||||
def post(self, post_data):
|
||||
return self.client.post(self.get_url("edit"), post_data)
|
||||
|
||||
def test_get_workflow_buttons_shown(self):
|
||||
response = self.get()
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(
|
||||
response,
|
||||
'<button type="submit" name="action-submit" value="Submit to Moderators approval" class="button">',
|
||||
count=1,
|
||||
)
|
||||
|
||||
@override_settings(WAGTAIL_WORKFLOW_ENABLED=False)
|
||||
def test_get_workflow_buttons_not_shown_when_workflow_disabled(self):
|
||||
response = self.get()
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertNotContains(response, 'name="action-submit"')
|
||||
|
||||
def test_post_submit_for_moderation(self):
|
||||
response = self.post({"text": "Edited!", "action-submit": "Submit"})
|
||||
self.object.refresh_from_db()
|
||||
|
||||
self.assertRedirects(response, self.get_url("list", ()))
|
||||
self.assertIsInstance(self.object, self.model)
|
||||
|
||||
# The live instance should still be live and should not be updated
|
||||
self.assertEqual(self.object.text, "I'm a full-featured snippet!")
|
||||
self.assertTrue(self.object.live)
|
||||
self.assertTrue(self.object.first_published_at)
|
||||
self.assertTrue(self.object.has_unpublished_changes)
|
||||
|
||||
# The object should now be in moderation
|
||||
self.assertEqual(
|
||||
self.object.current_workflow_state.status,
|
||||
WorkflowState.STATUS_IN_PROGRESS,
|
||||
)
|
||||
|
||||
# There should be a draft revision with the changes
|
||||
self.assertEqual(self.object.latest_revision.object_str, "Edited!")
|
||||
|
||||
# The current task state should point to the latest revision
|
||||
self.assertEqual(
|
||||
self.object.current_workflow_task_state.revision,
|
||||
self.object.latest_revision,
|
||||
)
|
||||
|
||||
|
||||
class TestEditViewNotLockable(TestEditView):
|
||||
model = ModeratedModel
|
||||
|
||||
|
||||
class TestWorkflowHistory(BaseWorkflowsTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.object.text = "Edited!"
|
||||
self.object.save_revision()
|
||||
self.workflow_state = self.workflow.start(self.object, self.user)
|
||||
|
||||
def test_get_index(self):
|
||||
response = self.client.get(self.get_url("workflow_history"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(
|
||||
response, "wagtailadmin/shared/workflow_history/index.html"
|
||||
)
|
||||
|
||||
self.assertContains(response, self.get_url("edit"))
|
||||
self.assertContains(
|
||||
response,
|
||||
self.get_url(
|
||||
"workflow_history_detail",
|
||||
(quote(self.object.pk), self.workflow_state.id),
|
||||
),
|
||||
)
|
||||
|
||||
# Should show the currently in progress workflow
|
||||
self.assertContains(response, "Moderators approval")
|
||||
self.assertContains(response, "In progress")
|
||||
self.assertContains(response, "test@email.com")
|
||||
|
||||
def test_get_index_with_bad_permissions(self):
|
||||
# Remove privileges from user
|
||||
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.get_url("workflow_history"))
|
||||
|
||||
self.assertRedirects(response, reverse("wagtailadmin_home"))
|
||||
|
||||
def test_get_detail(self):
|
||||
response = self.client.get(
|
||||
self.get_url(
|
||||
"workflow_history_detail",
|
||||
(quote(self.object.pk), self.workflow_state.id),
|
||||
),
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(
|
||||
response, "wagtailadmin/shared/workflow_history/detail.html"
|
||||
)
|
||||
|
||||
self.assertContains(response, self.get_url("edit"))
|
||||
self.assertContains(response, self.get_url("workflow_history"))
|
||||
|
||||
self.assertContains(response, '<div class="w-tabs" data-tabs>')
|
||||
self.assertContains(response, '<div class="tab-content">')
|
||||
self.assertContains(response, "Tasks")
|
||||
self.assertContains(response, "Timeline")
|
||||
|
||||
# Should show the currently in progress workflow with the latest revision
|
||||
self.assertContains(response, "Edited!")
|
||||
self.assertContains(response, "Moderators approval")
|
||||
self.assertContains(response, "In progress")
|
||||
self.assertContains(response, "test@email.com")
|
||||
|
||||
def test_get_detail_completed(self):
|
||||
self.workflow_state.current_task_state.approve(user=None)
|
||||
response = self.client.get(
|
||||
self.get_url(
|
||||
"workflow_history_detail",
|
||||
(quote(self.object.pk), self.workflow_state.id),
|
||||
),
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(
|
||||
response, "wagtailadmin/shared/workflow_history/detail.html"
|
||||
)
|
||||
|
||||
self.assertContains(response, self.get_url("edit"))
|
||||
self.assertContains(response, self.get_url("workflow_history"))
|
||||
|
||||
self.assertContains(response, '<div class="w-tabs" data-tabs>')
|
||||
self.assertContains(response, '<div class="tab-content">')
|
||||
self.assertContains(response, "Tasks")
|
||||
self.assertContains(response, "Timeline")
|
||||
|
||||
# Should show the completed workflow with the latest revision
|
||||
self.assertContains(response, "Edited!")
|
||||
self.assertContains(response, "Moderators approval")
|
||||
self.assertContains(response, "Workflow completed")
|
||||
self.assertContains(response, "test@email.com")
|
||||
self.assertNotContains(response, "In progress")
|
||||
|
||||
def test_get_detail_with_bad_permissions(self):
|
||||
# Remove privileges from user
|
||||
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.get_url(
|
||||
"workflow_history_detail",
|
||||
(quote(self.object.pk), self.workflow_state.id),
|
||||
),
|
||||
)
|
||||
|
||||
self.assertRedirects(response, reverse("wagtailadmin_home"))
|
||||
|
||||
def test_get_history_renders_comment(self):
|
||||
self.workflow_state.current_task_state.reject(comment="Can be better")
|
||||
# Ensure the comment in the log entry is rendered in the History view.
|
||||
# This is the main History view and not the Workflow History view, but
|
||||
# we test it here so we can reuse the workflow setup.
|
||||
response = self.client.get(self.get_url("history", (quote(self.object.pk),)))
|
||||
self.assertContains(
|
||||
response,
|
||||
"<div>Comment: <em>Can be better</em></div>",
|
||||
html=True,
|
||||
)
|
||||
|
||||
|
||||
class TestConfirmWorkflowCancellation(BaseWorkflowsTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.object.text = "Edited!"
|
||||
self.object.save_revision()
|
||||
self.workflow_state = self.workflow.start(self.object, self.user)
|
||||
|
||||
def test_get_confirm_workflow_cancellation(self):
|
||||
response = self.client.get(self.get_url("confirm_workflow_cancellation"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(
|
||||
response, "wagtailadmin/generic/confirm_workflow_cancellation.html"
|
||||
)
|
||||
self.assertContains(
|
||||
response,
|
||||
"Publishing this full-featured snippet will cancel the current workflow.",
|
||||
)
|
||||
self.assertContains(
|
||||
response, "Would you still like to publish this full-featured snippet?"
|
||||
)
|
||||
|
||||
@override_settings(WAGTAIL_WORKFLOW_CANCEL_ON_PUBLISH=False)
|
||||
def test_get_confirm_workflow_cancellation_with_disabled_setting(self):
|
||||
response = self.client.get(self.get_url("confirm_workflow_cancellation"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateNotUsed(
|
||||
response,
|
||||
"wagtailadmin/generic/confirm_workflow_cancellation.html",
|
||||
)
|
||||
self.assertJSONEqual(
|
||||
response.content.decode(),
|
||||
{"step": "no_confirmation_needed"},
|
||||
)
|
||||
Reference in New Issue
Block a user