Initial commit

This commit is contained in:
2024-08-27 20:33:44 +02:00
commit 1f1832267d
14794 changed files with 1599592 additions and 0 deletions

View File

@@ -0,0 +1,151 @@
import json
from django.urls import reverse
from wagtail.api.v2.tests.test_documents import (
TestDocumentDetail,
TestDocumentListing,
TestDocumentListingSearch,
)
from wagtail.documents.models import Document
from .utils import AdminAPITestCase
class TestAdminDocumentListing(AdminAPITestCase, TestDocumentListing):
fixtures = ["demosite.json"]
def get_response(self, **params):
return self.client.get(reverse("wagtailadmin_api:documents:listing"), params)
def get_document_id_list(self, content):
return [document["id"] for document in content["items"]]
# BASIC TESTS
def test_basic(self):
response = self.get_response()
self.assertEqual(response.status_code, 200)
self.assertEqual(response["Content-type"], "application/json")
# Will crash if the JSON is invalid
content = json.loads(response.content.decode("UTF-8"))
# Check that the meta section is there
self.assertIn("meta", content)
self.assertIsInstance(content["meta"], dict)
# Check that the total count is there and correct
self.assertIn("total_count", content["meta"])
self.assertIsInstance(content["meta"]["total_count"], int)
self.assertEqual(content["meta"]["total_count"], Document.objects.count())
# Check that the items section is there
self.assertIn("items", content)
self.assertIsInstance(content["items"], list)
# Check that each document has a meta section with type, detail_url and tags attributes
for document in content["items"]:
self.assertIn("meta", document)
self.assertIsInstance(document["meta"], dict)
self.assertEqual(
set(document["meta"].keys()),
{"type", "detail_url", "download_url", "tags"},
)
# Type should always be wagtaildocs.Document
self.assertEqual(document["meta"]["type"], "wagtaildocs.Document")
# Check detail_url
self.assertEqual(
document["meta"]["detail_url"],
"http://localhost/admin/api/main/documents/%d/" % document["id"],
)
# Check download_url
self.assertTrue(
document["meta"]["download_url"].startswith(
"http://localhost/documents/%d/" % document["id"]
)
)
# FIELDS
def test_fields_default(self):
response = self.get_response()
content = json.loads(response.content.decode("UTF-8"))
for document in content["items"]:
self.assertEqual(set(document.keys()), {"id", "meta", "title"})
self.assertEqual(
set(document["meta"].keys()),
{"type", "detail_url", "download_url", "tags"},
)
class TestAdminDocumentListingSearch(AdminAPITestCase, TestDocumentListingSearch):
fixtures = ["demosite.json"]
def get_response(self, **params):
return self.client.get(reverse("wagtailadmin_api:documents:listing"), params)
def get_document_id_list(self, content):
return [document["id"] for document in content["items"]]
class TestAdminDocumentDetail(AdminAPITestCase, TestDocumentDetail):
fixtures = ["demosite.json"]
def get_response(self, image_id, **params):
return self.client.get(
reverse("wagtailadmin_api:documents:detail", args=(image_id,)), params
)
def test_basic(self):
response = self.get_response(1)
self.assertEqual(response.status_code, 200)
self.assertEqual(response["Content-type"], "application/json")
# Will crash if the JSON is invalid
content = json.loads(response.content.decode("UTF-8"))
# Check the id field
self.assertIn("id", content)
self.assertEqual(content["id"], 1)
# Check that the meta section is there
self.assertIn("meta", content)
self.assertIsInstance(content["meta"], dict)
# Check the meta type
self.assertIn("type", content["meta"])
self.assertEqual(content["meta"]["type"], "wagtaildocs.Document")
# Check the meta detail_url
self.assertIn("detail_url", content["meta"])
self.assertEqual(
content["meta"]["detail_url"],
"http://localhost/admin/api/main/documents/1/",
)
# Check the meta download_url
self.assertIn("download_url", content["meta"])
self.assertEqual(
content["meta"]["download_url"],
"http://localhost/documents/1/wagtail_by_markyharky.jpg",
)
# Check the title field
self.assertIn("title", content)
self.assertEqual(content["title"], "Wagtail by mark Harkin")
# Check the tags field
self.assertIn("tags", content["meta"])
self.assertEqual(content["meta"]["tags"], [])
# Overwrite imported test cases do Django doesn't run them
TestDocumentDetail = None
TestDocumentListing = None

View File

@@ -0,0 +1,269 @@
import json
from django.urls import reverse
from wagtail.api.v2.tests.test_images import (
TestImageDetail,
TestImageListing,
TestImageListingSearch,
)
from wagtail.images import get_image_model
from wagtail.images.tests.utils import get_test_image_file
from .utils import AdminAPITestCase
class TestAdminImageListing(AdminAPITestCase, TestImageListing):
fixtures = ["demosite.json"]
def get_response(self, **params):
return self.client.get(reverse("wagtailadmin_api:images:listing"), params)
def get_image_id_list(self, content):
return [image["id"] for image in content["items"]]
# BASIC TESTS
def test_basic(self):
response = self.get_response()
self.assertEqual(response.status_code, 200)
self.assertEqual(response["Content-type"], "application/json")
# Will crash if the JSON is invalid
content = json.loads(response.content.decode("UTF-8"))
# Check that the meta section is there
self.assertIn("meta", content)
self.assertIsInstance(content["meta"], dict)
# Check that the total count is there and correct
self.assertIn("total_count", content["meta"])
self.assertIsInstance(content["meta"]["total_count"], int)
self.assertEqual(
content["meta"]["total_count"], get_image_model().objects.count()
)
# Check that the items section is there
self.assertIn("items", content)
self.assertIsInstance(content["items"], list)
# Check that each image has a meta section with type, detail_url and tags attributes
for image in content["items"]:
self.assertIn("meta", image)
self.assertIsInstance(image["meta"], dict)
self.assertEqual(
set(image["meta"].keys()),
{"type", "detail_url", "tags", "download_url"},
)
# Type should always be wagtailimages.Image
self.assertEqual(image["meta"]["type"], "wagtailimages.Image")
# Check detail url
self.assertEqual(
image["meta"]["detail_url"],
"http://localhost/admin/api/main/images/%d/" % image["id"],
)
# FIELDS
def test_fields_default(self):
response = self.get_response()
content = json.loads(response.content.decode("UTF-8"))
for image in content["items"]:
self.assertEqual(
set(image.keys()),
{"id", "meta", "title", "width", "height", "thumbnail"},
)
self.assertEqual(
set(image["meta"].keys()),
{"type", "detail_url", "download_url", "tags"},
)
def test_fields(self):
response = self.get_response(fields="width,height")
content = json.loads(response.content.decode("UTF-8"))
for image in content["items"]:
self.assertEqual(
set(image.keys()),
{"id", "meta", "title", "width", "height", "thumbnail"},
)
self.assertEqual(
set(image["meta"].keys()),
{"type", "detail_url", "download_url", "tags"},
)
def test_remove_fields(self):
response = self.get_response(fields="-title")
content = json.loads(response.content.decode("UTF-8"))
for image in content["items"]:
self.assertEqual(
set(image.keys()), {"id", "meta", "width", "height", "thumbnail"}
)
def test_remove_meta_fields(self):
response = self.get_response(fields="-tags")
content = json.loads(response.content.decode("UTF-8"))
for image in content["items"]:
self.assertEqual(
set(image.keys()),
{"id", "meta", "title", "width", "height", "thumbnail"},
)
self.assertEqual(
set(image["meta"].keys()), {"type", "detail_url", "download_url"}
)
def test_remove_all_meta_fields(self):
response = self.get_response(fields="-type,-detail_url,-tags")
content = json.loads(response.content.decode("UTF-8"))
for image in content["items"]:
self.assertEqual(
set(image.keys()),
{"id", "title", "width", "height", "thumbnail", "meta"},
)
def test_remove_id_field(self):
response = self.get_response(fields="-id")
content = json.loads(response.content.decode("UTF-8"))
for image in content["items"]:
self.assertEqual(
set(image.keys()), {"meta", "title", "width", "height", "thumbnail"}
)
def test_all_fields(self):
response = self.get_response(fields="*")
content = json.loads(response.content.decode("UTF-8"))
for image in content["items"]:
self.assertEqual(
set(image.keys()),
{"id", "meta", "title", "width", "height", "thumbnail"},
)
self.assertEqual(
set(image["meta"].keys()),
{"type", "detail_url", "tags", "download_url"},
)
def test_all_fields_then_remove_something(self):
response = self.get_response(fields="*,-title,-tags")
content = json.loads(response.content.decode("UTF-8"))
for image in content["items"]:
self.assertEqual(
set(image.keys()), {"id", "meta", "width", "height", "thumbnail"}
)
self.assertEqual(
set(image["meta"].keys()), {"type", "detail_url", "download_url"}
)
def test_fields_tags(self):
response = self.get_response(fields="tags")
content = json.loads(response.content.decode("UTF-8"))
for image in content["items"]:
self.assertEqual(
set(image.keys()),
{"id", "meta", "title", "width", "height", "thumbnail"},
)
self.assertEqual(
set(image["meta"].keys()),
{"type", "detail_url", "tags", "download_url"},
)
self.assertIsInstance(image["meta"]["tags"], list)
class TestAdminImageListingSearch(AdminAPITestCase, TestImageListingSearch):
fixtures = ["demosite.json"]
def get_response(self, **params):
return self.client.get(reverse("wagtailadmin_api:images:listing"), params)
def get_image_id_list(self, content):
return [image["id"] for image in content["items"]]
class TestAdminImageDetail(AdminAPITestCase, TestImageDetail):
fixtures = ["demosite.json"]
def get_response(self, image_id, **params):
return self.client.get(
reverse("wagtailadmin_api:images:detail", args=(image_id,)), params
)
def test_basic(self):
response = self.get_response(5)
self.assertEqual(response.status_code, 200)
self.assertEqual(response["Content-type"], "application/json")
# Will crash if the JSON is invalid
content = json.loads(response.content.decode("UTF-8"))
# Check the id field
self.assertIn("id", content)
self.assertEqual(content["id"], 5)
# Check that the meta section is there
self.assertIn("meta", content)
self.assertIsInstance(content["meta"], dict)
# Check the meta type
self.assertIn("type", content["meta"])
self.assertEqual(content["meta"]["type"], "wagtailimages.Image")
# Check the meta detail_url
self.assertIn("detail_url", content["meta"])
self.assertEqual(
content["meta"]["detail_url"], "http://localhost/admin/api/main/images/5/"
)
# Check the thumbnail
# Note: This is None because the source image doesn't exist
# See test_thumbnail below for working example
self.assertIn("thumbnail", content)
self.assertEqual(content["thumbnail"], {"error": "SourceImageIOError"})
# Check the title field
self.assertIn("title", content)
self.assertEqual(content["title"], "James Joyce")
# Check the width and height fields
self.assertIn("width", content)
self.assertIn("height", content)
self.assertEqual(content["width"], 500)
self.assertEqual(content["height"], 392)
# Check the tags field
self.assertIn("tags", content["meta"])
self.assertEqual(content["meta"]["tags"], [])
def test_thumbnail(self):
# Add a new image with source file
image = get_image_model().objects.create(
title="Test image",
file=get_test_image_file(),
)
response = self.get_response(image.id)
content = json.loads(response.content.decode("UTF-8"))
self.assertIn("thumbnail", content)
self.assertEqual(content["thumbnail"]["width"], 165)
self.assertEqual(content["thumbnail"]["height"], 123)
self.assertTrue(content["thumbnail"]["url"].startswith("/media/images/test"))
# Check that source_image_error didn't appear
self.assertNotIn("source_image_error", content["meta"])
# Overwrite imported test cases do Django doesn't run them
TestImageDetail = None
TestImageListing = None

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
from django.test import TransactionTestCase
from wagtail.test.utils import WagtailTestUtils
class AdminAPITestCase(WagtailTestUtils, TransactionTestCase):
def setUp(self):
super().setUp()
self.user = self.login()

View File

@@ -0,0 +1,96 @@
from django.test import TestCase
from django.urls import reverse
from django.utils import timezone
from wagtail.models import Page, Site
from wagtail.test.benchmark import Benchmark
from wagtail.test.testapp.models import SingleEventPage, StreamPage
from wagtail.test.utils import WagtailTestUtils
class BenchPageExplorerWith50LargePages(Benchmark, WagtailTestUtils, TestCase):
"""
Creates 50 pages with large body content and benches the explorer.
This will be slow if the body content is being loaded from the database.
"""
def setUp(self):
self.root_page = Page.objects.get(id=1)
# Add a site so the URLs render correctly
Site.objects.create(is_default_site=True, root_page=self.root_page)
# Create a large piece of body text
body = (
"["
+ ",".join(['{"type": "text", "value": "%s"}' % ("foo" * 2000)] * 100)
+ "]"
)
# Create 50 simple pages with long content fields
for i in range(50):
self.root_page.add_child(
instance=StreamPage(
title=f"Page {i + 1}",
slug=str(i + 1),
body=body,
)
)
self.login()
def bench(self):
response = self.client.get(
reverse("wagtailadmin_explore", args=(self.root_page.id,))
)
# Check the response was good
self.assertEqual(response.status_code, 200)
# Check every single page was rendered
self.assertContains(response, "Page 1")
self.assertContains(response, "Page 49")
class BenchPageExplorerWithCustomURLPages(Benchmark, WagtailTestUtils, TestCase):
"""
Creates 50 pages of a class with a customised the .url property.
This will check how long it takes to generate URLs for all of these
pages.
"""
def setUp(self):
self.root_page = Page.objects.get(id=1)
# Add a site so the URLs render correctly
Site.objects.create(is_default_site=True, root_page=self.root_page)
# Create 50 blog pages
for i in range(50):
self.root_page.add_child(
instance=SingleEventPage(
title=f"Event {i + 1}",
slug=str(i + 1),
date_from=timezone.now(),
audience="public",
location="reykjavik",
cost="cost",
)
)
self.login()
def bench(self):
response = self.client.get(
reverse("wagtailadmin_explore", args=(self.root_page.id,))
)
# Check the response was good
self.assertEqual(response.status_code, 200)
# Check every single page was rendered
self.assertContains(response, "Event 1")
self.assertContains(response, "Event 49")
# Check the URLs were rendered correctly
self.assertContains(response, 'a href="http:///49/pointless-suffix/"')

View File

@@ -0,0 +1 @@
DATETIME_INPUT_FORMATS = ["%d.%m.%Y. %H:%M"]

View File

@@ -0,0 +1,34 @@
from django.test import TestCase
from django.urls import reverse
from wagtail.test.utils import WagtailTestUtils
class TestBulkActionDispatcher(WagtailTestUtils, TestCase):
def setUp(self):
# Login
self.user = self.login()
def test_bulk_action_invalid_action(self):
url = reverse(
"wagtail_bulk_action",
args=(
"wagtailcore",
"page",
"ships",
),
)
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
def test_bulk_action_invalid_model(self):
url = reverse(
"wagtail_bulk_action",
args=(
"doesnotexist",
"doesnotexist",
"doesnotexist",
),
)
response = self.client.get(url)
self.assertEqual(response.status_code, 404)

View File

@@ -0,0 +1,364 @@
from unittest import mock
from django.contrib.auth.models import Permission
from django.db.models.signals import post_delete, pre_delete
from django.http import HttpRequest
from django.http.response import HttpResponse
from django.test import TestCase
from django.urls import reverse
from wagtail.admin.views.pages.bulk_actions.page_bulk_action import PageBulkAction
from wagtail.models import Page
from wagtail.signals import page_unpublished
from wagtail.test.testapp.models import SimplePage
from wagtail.test.utils import WagtailTestUtils
class TestBulkDelete(WagtailTestUtils, TestCase):
def setUp(self):
# Find root page
self.root_page = Page.objects.get(id=2)
# Add child pages
self.child_pages = [
SimplePage(
title=f"Hello world!-{i}", slug=f"hello-world-{i}", content=f"hello-{i}"
)
for i in range(1, 5)
]
# first three child pages will be deleted
self.pages_to_be_deleted = self.child_pages[:3]
self.pages_not_to_be_deleted = self.child_pages[3:]
for child_page in self.child_pages:
self.root_page.add_child(instance=child_page)
# map of the form { page: [child_pages] } to be added
self.grandchildren_pages = {
self.pages_to_be_deleted[0]: [
SimplePage(
title="Hello world!-a", slug="hello-world-a", content="hello-a"
)
],
self.pages_to_be_deleted[1]: [
SimplePage(
title="Hello world!-b", slug="hello-world-b", content="hello-b"
),
SimplePage(
title="Hello world!-c", slug="hello-world-c", content="hello-c"
),
],
}
for child_page, grandchild_pages in self.grandchildren_pages.items():
for grandchild_page in grandchild_pages:
child_page.add_child(instance=grandchild_page)
self.url = (
reverse(
"wagtail_bulk_action",
args=(
"wagtailcore",
"page",
"delete",
),
)
+ "?"
)
for child_page in self.pages_to_be_deleted:
self.url += f"&id={child_page.id}"
# Login
self.user = self.login()
def test_bulk_delete_get(self):
response = self.client.get(self.url)
self.assertEqual(response.status_code, 200)
# deletion should not actually happen on GET
for child_page in self.child_pages:
self.assertTrue(SimplePage.objects.filter(id=child_page.id).exists())
html = response.content.decode()
for child_page in self.pages_to_be_deleted:
# check if the pages to be deleted and number of descendant pages are displayed
needle = "<li>"
needle += '<a href="{edit_page_url}" target="_blank" rel="noreferrer">{page_title}</a>'.format(
edit_page_url=reverse("wagtailadmin_pages:edit", args=[child_page.id]),
page_title=child_page.title,
)
descendants = len(self.grandchildren_pages.get(child_page, []))
if descendants:
needle += "<p>"
if descendants == 1:
needle += "This will also delete one more subpage."
else:
needle += f"This will also delete {descendants} more subpages."
needle += "</p>"
needle += "</li>"
self.assertInHTML(needle, html)
def test_page_delete_specific_admin_title(self):
response = self.client.get(self.url)
self.assertEqual(response.status_code, 200)
# The number of pages to be deleted is shown on the delete confirmation page.
self.assertContains(response, f"Delete {len(self.pages_to_be_deleted)} pages")
def test_page_delete_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()
# Get delete page
response = self.client.get(self.url)
# Check that the user received a 200 redirect response
self.assertEqual(response.status_code, 200)
# Check that the deletion has not happened
for child_page in self.child_pages:
self.assertTrue(SimplePage.objects.filter(id=child_page.id).exists())
html = response.content.decode()
self.assertInHTML(
"<p>You don't have permission to delete these pages</p>", html
)
for child_page in self.pages_to_be_deleted:
self.assertInHTML(f"<li>{child_page.title}</li>", html)
self.assertTagInHTML(
f"""<form action="{self.url}" method="POST"></form>""",
html,
count=0,
)
def test_bulk_delete_post(self):
# Connect a mock signal handler to page_unpublished signal
mock_handler = mock.MagicMock()
page_unpublished.connect(mock_handler)
try:
# Post
response = self.client.post(self.url)
# Should be redirected to explorer page
self.assertEqual(response.status_code, 302)
# treebeard should report no consistency problems with the tree
self.assertFalse(
any(Page.find_problems()), msg="treebeard found consistency problems"
)
# Check that the child pages to be deleted are gone
for child_page in self.pages_to_be_deleted:
self.assertFalse(SimplePage.objects.filter(id=child_page.id).exists())
# Check that the child pages not to be deleted remain
for child_page in self.pages_not_to_be_deleted:
self.assertTrue(SimplePage.objects.filter(id=child_page.id).exists())
# Check that the page_unpublished signal was fired for all pages
num_descendants = sum(len(i) for i in self.grandchildren_pages.values())
self.assertEqual(
mock_handler.call_count, len(self.pages_to_be_deleted) + num_descendants
)
i = 0
for child_page in self.pages_to_be_deleted:
mock_call = mock_handler.mock_calls[i][2]
i += 1
self.assertEqual(mock_call["sender"], child_page.specific_class)
self.assertEqual(mock_call["instance"], child_page)
self.assertIsInstance(mock_call["instance"], child_page.specific_class)
for grandchildren_page in self.grandchildren_pages.get(child_page, []):
mock_call = mock_handler.mock_calls[i][2]
i += 1
self.assertEqual(
mock_call["sender"], grandchildren_page.specific_class
)
self.assertEqual(mock_call["instance"], grandchildren_page)
self.assertIsInstance(
mock_call["instance"], grandchildren_page.specific_class
)
finally:
page_unpublished.disconnect(mock_handler)
def test_bulk_delete_notlive_post(self):
# Same as above, but this makes sure the page_unpublished signal is not fired
# for the page that is not live when it is deleted
# Unpublish the first child page
page_to_be_unpublished = self.pages_to_be_deleted[0]
page_to_be_unpublished.unpublish(user=self.user)
# Connect a mock signal handler to page_unpublished signal
mock_handler = mock.MagicMock()
page_unpublished.connect(mock_handler)
try:
# Post
response = self.client.post(self.url)
# Should be redirected to explorer page
self.assertEqual(response.status_code, 302)
# treebeard should report no consistency problems with the tree
self.assertFalse(
any(Page.find_problems()), msg="treebeard found consistency problems"
)
# Check that the child pages to be deleted are gone
for child_page in self.pages_to_be_deleted:
self.assertFalse(SimplePage.objects.filter(id=child_page.id).exists())
# Check that the child pages not to be deleted remain
for child_page in self.pages_not_to_be_deleted:
self.assertTrue(SimplePage.objects.filter(id=child_page.id).exists())
# Check that the page_unpublished signal was not fired
num_descendants = sum(len(v) for v in self.grandchildren_pages.values())
self.assertEqual(
mock_handler.call_count,
len(self.pages_to_be_deleted) + num_descendants - 1,
)
# check that only signals for other pages are fired
i = 0
for child_page in self.pages_to_be_deleted:
if child_page.id != page_to_be_unpublished.id:
mock_call = mock_handler.mock_calls[i][2]
i += 1
self.assertEqual(mock_call["sender"], child_page.specific_class)
self.assertEqual(mock_call["instance"], child_page)
self.assertIsInstance(
mock_call["instance"], child_page.specific_class
)
for grandchildren_page in self.grandchildren_pages.get(child_page, []):
mock_call = mock_handler.mock_calls[i][2]
i += 1
self.assertEqual(
mock_call["sender"], grandchildren_page.specific_class
)
self.assertEqual(mock_call["instance"], grandchildren_page)
self.assertIsInstance(
mock_call["instance"], grandchildren_page.specific_class
)
finally:
page_unpublished.disconnect(mock_handler)
def test_subpage_deletion(self):
# Connect mock signal handlers to page_unpublished, pre_delete and post_delete signals
unpublish_signals_received = []
pre_delete_signals_received = []
post_delete_signals_received = []
def page_unpublished_handler(sender, instance, **kwargs):
unpublish_signals_received.append((sender, instance.pk))
def pre_delete_handler(sender, instance, **kwargs):
pre_delete_signals_received.append((sender, instance.pk))
def post_delete_handler(sender, instance, **kwargs):
post_delete_signals_received.append((sender, instance.pk))
page_unpublished.connect(page_unpublished_handler)
pre_delete.connect(pre_delete_handler)
post_delete.connect(post_delete_handler)
try:
# Post
response = self.client.post(self.url)
# Should be redirected to explorer page
self.assertEqual(response.status_code, 302)
# treebeard should report no consistency problems with the tree
self.assertFalse(
any(Page.find_problems()), msg="treebeard found consistency problems"
)
# Check that the child pages to be deleted are gone
for child_page in self.pages_to_be_deleted:
self.assertFalse(SimplePage.objects.filter(id=child_page.id).exists())
# Check that the child pages not to be deleted remain
for child_page in self.pages_not_to_be_deleted:
self.assertTrue(SimplePage.objects.filter(id=child_page.id).exists())
# Check that the subpages are also gone
for grandchild_pages in self.grandchildren_pages.values():
for grandchild_page in grandchild_pages:
self.assertFalse(
SimplePage.objects.filter(id=grandchild_page.id).exists()
)
# Check that the signals were fired for all child and grandchild pages
for child_page, grandchild_pages in self.grandchildren_pages.items():
self.assertIn((SimplePage, child_page.id), unpublish_signals_received)
self.assertIn((SimplePage, child_page.id), pre_delete_signals_received)
self.assertIn((SimplePage, child_page.id), post_delete_signals_received)
for grandchild_page in grandchild_pages:
self.assertIn(
(SimplePage, grandchild_page.id), unpublish_signals_received
)
self.assertIn(
(SimplePage, grandchild_page.id), pre_delete_signals_received
)
self.assertIn(
(SimplePage, grandchild_page.id), post_delete_signals_received
)
self.assertEqual(response.status_code, 302)
finally:
page_unpublished.disconnect(page_unpublished_handler)
pre_delete.disconnect(pre_delete_handler)
post_delete.disconnect(post_delete_handler)
def test_before_delete_page_hook(self):
def hook_func(request, action_type, pages, action_class_instance):
self.assertEqual(action_type, "delete")
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(action_class_instance, PageBulkAction)
for i, page in enumerate(pages):
self.assertEqual(page.id, self.pages_to_be_deleted[i].id)
return HttpResponse("Overridden!")
with self.register_hook("before_bulk_action", hook_func):
response = self.client.post(self.url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# Check that the child pages are not deleted
for child_page in self.child_pages:
self.assertTrue(SimplePage.objects.filter(id=child_page.id).exists())
def test_after_delete_page_hook(self):
def hook_func(request, action_type, pages, action_class_instance):
self.assertEqual(action_type, "delete")
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(action_class_instance, PageBulkAction)
for i, page in enumerate(pages):
self.assertEqual(page.id, self.pages_to_be_deleted[i].id)
return HttpResponse("Overridden!")
with self.register_hook("after_bulk_action", hook_func):
response = self.client.post(self.url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# Check that the child pages to be deleted are gone
for child_page in self.pages_to_be_deleted:
self.assertFalse(SimplePage.objects.filter(id=child_page.id).exists())
# Check that the child pages not to be deleted remain
for child_page in self.pages_not_to_be_deleted:
self.assertTrue(SimplePage.objects.filter(id=child_page.id).exists())

View File

@@ -0,0 +1,321 @@
from unittest import mock
from django.contrib.auth import get_user_model
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.admin.views.pages.bulk_actions.page_bulk_action import PageBulkAction
from wagtail.models import Page
from wagtail.signals import post_page_move, pre_page_move
from wagtail.test.testapp.models import BusinessChild, SimplePage
from wagtail.test.utils import WagtailTestUtils
class TestBulkMove(WagtailTestUtils, TestCase):
fixtures = ["test.json"]
def setUp(self):
# Find root page
self.root_page = Page.objects.get(id=2)
# Create three sections
self.section_a = SimplePage(
title="Section A", slug="section-a", content="hello"
)
self.root_page.add_child(instance=self.section_a)
self.section_b = SimplePage(
title="Section B", slug="section-b", content="hello"
)
self.root_page.add_child(instance=self.section_b)
self.section_c = SimplePage(
title="Section C", slug="section-c", content="hello"
)
self.root_page.add_child(instance=self.section_c)
# Add test page A into section A
self.test_page_a = SimplePage(
title="Hello world!", slug="hello-world-a", content="hello"
)
self.section_a.add_child(instance=self.test_page_a)
# Add test page B into section C
self.test_page_b = SimplePage(
title="Hello world!", slug="hello-world-b", content="hello"
)
self.section_c.add_child(instance=self.test_page_b)
# Add test page B_1 into section C
self.test_page_b_1 = SimplePage(
title="Hello world!", slug="hello-world-b-1", content="hello"
)
self.section_c.add_child(instance=self.test_page_b_1)
# Add test page A_1 into section C having same slug as test page A
self.test_page_a_1 = SimplePage(
title="Hello world!", slug="hello-world-a", content="hello"
)
self.section_c.add_child(instance=self.test_page_a_1)
# Add unpublished page to the root with a child page
self.unpublished_page = SimplePage(
title="Unpublished", slug="unpublished", content="hello"
)
sub_page = SimplePage(title="Sub Page", slug="sub-page", content="child")
self.root_page.add_child(instance=self.unpublished_page)
self.unpublished_page.add_child(instance=sub_page)
# unpublish pages last (used to validate the edit only permission)
self.unpublished_page.unpublish()
sub_page.unpublish()
self.pages_to_be_moved = [self.test_page_b, self.test_page_b_1]
self.url = (
reverse(
"wagtail_bulk_action",
args=(
"wagtailcore",
"page",
"move",
),
)
+ f"?id={self.test_page_b.id}&id={self.test_page_b_1.id}"
)
# Login
self.user = self.login()
def test_bulk_move(self):
response = self.client.get(self.url)
self.assertEqual(response.status_code, 200)
html = response.content.decode()
self.assertInHTML("<p>Are you sure you want to move these pages?</p>", html)
self.assertInHTML(
'<li><a href="{edit_page_url}" target="_blank" rel="noreferrer">Hello world! (simple page)</a></li>'.format(
edit_page_url=reverse(
"wagtailadmin_pages:edit", args=[self.test_page_b.id]
),
),
html,
)
def test_bulk_move_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()
# Get move page
response = self.client.get(self.url)
self.assertEqual(response.status_code, 200)
html = response.content.decode()
self.assertInHTML("<p>You don't have permission to move these pages</p>", html)
for child_page in self.pages_to_be_moved:
self.assertInHTML(f"<li>{child_page.title}</li>", html)
self.assertTagInHTML(
f"""<form action="{self.url}" method="POST"></form>""",
html,
count=0,
)
def test_user_without_bulk_delete_permission_can_move(self):
# to verify that a user without bulk delete permission is able to move a page with a child page
self.client.logout()
user = get_user_model().objects.get(email="siteeditor@example.com")
self.login(user)
# ensure the bulk_delete is not applicable to this user
can_bulk_delete = self.test_page_b.permissions_for_user(user).can_delete()
self.assertFalse(can_bulk_delete)
response = self.client.get(
reverse(
"wagtail_bulk_action",
args=(
"wagtailcore",
"page",
"move",
),
)
+ f"?id={self.unpublished_page.id}"
)
self.assertEqual(response.status_code, 200)
def test_bulk_move_destination_not_allowed(self):
page = BusinessChild(title="Section no child", slug="section-no-child")
self.root_page.add_child(instance=page)
response = self.client.post(self.url, {"chooser": page.id})
html = response.content.decode()
self.assertInHTML(
f"<p>The following pages cannot be moved to {page.title}</p>", html
)
for child_page in self.pages_to_be_moved:
self.assertInHTML(
'<li><a href="{edit_page_url}" target="_blank" rel="noreferrer">{page_title}</a></li>'.format(
edit_page_url=reverse(
"wagtailadmin_pages:edit", args=[child_page.id]
),
page_title=child_page.title,
),
html,
)
def test_bulk_move_slug_already_taken(self):
temp_page_1 = SimplePage(
title="Hello world!", slug="hello-world-b", content="hello"
)
temp_page_2 = SimplePage(
title="Hello world!", slug="hello-world-b-1", content="hello"
)
self.section_b.add_child(instance=temp_page_1)
self.section_b.add_child(instance=temp_page_2)
response = self.client.post(self.url, {"chooser": self.section_b.id})
html = response.content.decode()
self.assertInHTML(
"<p>The following pages cannot be moved due to duplicate slugs</p>", html
)
for child_page in self.pages_to_be_moved:
self.assertInHTML(
'<li><a href="{edit_page_url}" target="_blank" rel="noreferrer">{page_title}</a></li>'.format(
edit_page_url=reverse(
"wagtailadmin_pages:edit", args=[child_page.id]
),
page_title=child_page.title,
),
html,
)
def test_bulk_move_triggers_signals(self):
# Connect a mock signal handler to pre_page_move and post_page_move signals
pre_moved_handler = mock.MagicMock()
post_moved_handler = mock.MagicMock()
pre_page_move.connect(pre_moved_handler)
post_page_move.connect(post_moved_handler)
# Post to view confirm move page
try:
self.client.post(self.url, {"chooser": self.section_b.id})
finally:
# Disconnect mock handler to prevent cross-test pollution
pre_page_move.disconnect(pre_moved_handler)
post_page_move.disconnect(post_moved_handler)
# Check that the pre_page_move signals were fired
self.assertTrue(
pre_moved_handler.mock_calls[0].called_with(
sender=self.test_page_b.specific_class,
instance=self.test_page_b,
parent_page_before=self.section_c,
parent_page_after=self.section_b,
url_path_before="/home/section-c/hello-world-b/",
url_path_after="/home/section-b/hello-world-b/",
)
)
self.assertTrue(
pre_moved_handler.mock_calls[1].called_with(
sender=self.test_page_b_1.specific_class,
instance=self.test_page_b_1,
parent_page_before=self.section_c,
parent_page_after=self.section_b,
url_path_before="/home/section-c/hello-world-b-1/",
url_path_after="/home/section-b/hello-world-b-1/",
)
)
# Check that the post_page_move signals were fired
self.assertTrue(
post_moved_handler.mock_calls[0].called_with(
sender=self.test_page_b.specific_class,
instance=self.test_page_b,
parent_page_before=self.section_c,
parent_page_after=self.section_b,
url_path_before="/home/section-c/hello-world-b/",
url_path_after="/home/section-b/hello-world-b/",
)
)
self.assertTrue(
post_moved_handler.mock_calls[1].called_with(
sender=self.test_page_b_1.specific_class,
instance=self.test_page_b_1,
parent_page_before=self.section_c,
parent_page_after=self.section_b,
url_path_before="/home/section-c/hello-world-b-1/",
url_path_after="/home/section-b/hello-world-b-1/",
)
)
def test_before_bulk_move_hook(self):
def hook_func(request, action_type, pages, action_class_instance):
self.assertEqual(action_type, "move")
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(action_class_instance, PageBulkAction)
for i, page in enumerate(pages):
self.assertEqual(page.id, self.pages_to_be_moved[i].id)
return HttpResponse("Overridden!")
with self.register_hook("before_bulk_action", hook_func):
response = self.client.post(self.url, {"chooser": self.section_b.id})
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
self.assertEqual(
Page.objects.get(id=self.test_page_b.id).get_parent().id, self.section_c.id
)
self.assertEqual(
Page.objects.get(id=self.test_page_b_1.id).get_parent().id,
self.section_c.id,
)
def test_after_bulk_move_hook(self):
def hook_func(request, action_type, pages, action_class_instance):
self.assertEqual(action_type, "move")
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(action_class_instance, PageBulkAction)
for i, page in enumerate(pages):
self.assertEqual(page.id, self.pages_to_be_moved[i].id)
return HttpResponse("Overridden!")
with self.register_hook("after_bulk_action", hook_func):
response = self.client.post(self.url, {"chooser": self.section_b.id})
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# pages should be moved
self.assertEqual(
Page.objects.get(id=self.test_page_b.id).get_parent().id, self.section_b.id
)
self.assertEqual(
Page.objects.get(id=self.test_page_b_1.id).get_parent().id,
self.section_b.id,
)

View File

@@ -0,0 +1,381 @@
from unittest import mock
from django.contrib.auth.models import Permission
from django.http import HttpRequest, HttpResponse
from django.test import TestCase
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from wagtail.admin.views.pages.bulk_actions.page_bulk_action import PageBulkAction
from wagtail.models import Page
from wagtail.signals import page_published
from wagtail.test.testapp.models import SimplePage
from wagtail.test.utils import WagtailTestUtils
class TestBulkPublish(WagtailTestUtils, TestCase):
def setUp(self):
self.root_page = Page.objects.get(id=2)
# Add child pages which will have already been published before we
# bulk publish them.
self.child_pages = [
SimplePage(
title=f"Hello world!-{i}",
slug=f"hello-world-{i}",
content=f"Hello world {i}!",
live=False,
)
for i in range(1, 5)
]
self.pages_to_be_published = self.child_pages[:3]
self.pages_not_to_be_published = self.child_pages[3:]
for child_page in self.child_pages:
self.root_page.add_child(instance=child_page)
for i, child_page in enumerate(self.child_pages):
child_page.content = f"Hello published world {i}!"
child_page.save_revision()
# Add an additional child page which will be bulk published from a
# draft-only state.
draft_page = SimplePage(
title="Hello world!-5",
slug="hello-world-5",
content="Hello published world 5!",
live=False,
)
self.root_page.add_child(instance=draft_page)
self.child_pages.append(draft_page)
self.pages_to_be_published.append(draft_page)
self.url = (
reverse(
"wagtail_bulk_action",
args=(
"wagtailcore",
"page",
"publish",
),
)
+ "?"
)
for child_page in self.pages_to_be_published:
self.url += f"id={child_page.id}&"
self.redirect_url = reverse("wagtailadmin_explore", args=(self.root_page.id,))
self.user = self.login()
def test_publish_view(self):
"""
This tests that the publish view responds with a publish confirm page
"""
# Request confirm publish page
response = self.client.get(self.url)
# # Check that the user received a publish confirm page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(
response, "wagtailadmin/pages/bulk_actions/confirm_bulk_publish.html"
)
# Page titles shown on the confirmation page should use SimplePage's custom get_admin_display_title method
self.assertContains(response, "Hello world!-1 (simple page)")
def test_publish_view_invalid_page_id(self):
"""
This tests that the publish view returns an error if the page id is invalid
"""
# Request confirm publish page but with illegal page id
response = self.client.get(
reverse(
"wagtail_bulk_action",
args=(
"wagtailcore",
"page",
"publish",
),
)
)
# Check that the user received a 404 response
self.assertEqual(response.status_code, 404)
def test_publish_view_bad_permissions(self):
"""
This tests that the publish view doesn't allow users without publish permissions
"""
# 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()
# Request confirm publish page
response = self.client.get(self.url)
# Check that the user received a 200 redirected response
self.assertEqual(response.status_code, 200)
html = response.content.decode()
self.assertInHTML(
"<p>You don't have permission to publish these pages</p>", html
)
for child_page in self.pages_to_be_published:
self.assertInHTML(f"<li>{child_page.title}</li>", html)
def test_publish_view_post(self):
"""
This posts to the publish view and checks that the page was published
"""
# Connect a mock signal handler to page_published signal
mock_handler = mock.MagicMock()
page_published.connect(mock_handler)
try:
# Post to the publish page
response = self.client.post(self.url)
# Should be redirected to explorer page
self.assertEqual(response.status_code, 302)
# Check that the child pages were published
for child_page in self.pages_to_be_published:
published_page = SimplePage.objects.get(id=child_page.id)
self.assertTrue(published_page.live)
self.assertIn("Hello published", published_page.content)
# Check that the child pages not to be published remain
for child_page in self.pages_not_to_be_published:
self.assertFalse(Page.objects.get(id=child_page.id).live)
# Check that the page_published signal was fired
self.assertEqual(mock_handler.call_count, len(self.pages_to_be_published))
for i, child_page in enumerate(self.pages_to_be_published):
mock_call = mock_handler.mock_calls[i][2]
self.assertEqual(mock_call["sender"], child_page.specific_class)
self.assertEqual(mock_call["instance"], child_page)
self.assertIsInstance(mock_call["instance"], child_page.specific_class)
finally:
page_published.disconnect(mock_handler)
def test_after_publish_page(self):
def hook_func(request, action_type, pages, action_class_instance):
self.assertEqual(action_type, "publish")
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(action_class_instance, PageBulkAction)
for i, page in enumerate(pages):
self.assertEqual(page.id, self.pages_to_be_published[i].id)
return HttpResponse("Overridden!")
with self.register_hook("after_bulk_action", hook_func):
response = self.client.post(self.url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
for child_page in self.pages_to_be_published:
child_page.refresh_from_db()
self.assertEqual(child_page.status_string, _("live"))
def test_before_publish_page(self):
def hook_func(request, action_type, pages, action_class_instance):
self.assertEqual(action_type, "publish")
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(action_class_instance, PageBulkAction)
for i, page in enumerate(pages):
self.assertEqual(page.id, self.pages_to_be_published[i].id)
return HttpResponse("Overridden!")
with self.register_hook("before_bulk_action", hook_func):
response = self.client.post(self.url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
def test_publish_descendants_view(self):
"""
This tests that the publish view responds with a publish confirm page that does not contain the form field 'include_descendants'
"""
# Get publish page for page with no descendants
response = self.client.get(self.url)
# Check that the user received a publish confirm page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(
response, "wagtailadmin/pages/bulk_actions/confirm_bulk_publish.html"
)
# Check the form does not contain the checkbox field include_descendants
self.assertNotContains(
response,
'name="include_descendants"',
)
class TestBulkPublishIncludingDescendants(WagtailTestUtils, TestCase):
def setUp(self):
self.root_page = Page.objects.get(id=2)
# Add child pages
self.child_pages = [
SimplePage(
title=f"Hello world!-{i}",
slug=f"hello-world-{i}",
content=f"Hello world {i}!",
live=False,
)
for i in range(1, 5)
]
self.pages_to_be_published = self.child_pages[:3]
self.pages_not_to_be_published = self.child_pages[3:]
for child_page in self.child_pages:
self.root_page.add_child(instance=child_page)
for i, child_page in enumerate(self.child_pages):
child_page.content = f"Hello updated world {i}!"
child_page.save_revision()
# Add grandchild pages which will have already been published before
# we bulk publish them.
# map of the form { page: [child_pages] } to be added
self.grandchildren_pages = {
self.pages_to_be_published[0]: [
SimplePage(
title="Hello world!-a",
slug="hello-world-a",
content="Hello world a!",
live=False,
)
],
self.pages_to_be_published[1]: [
SimplePage(
title="Hello world!-b",
slug="hello-world-b",
content="Hello world b!",
live=False,
),
SimplePage(
title="Hello world!-c",
slug="hello-world-c",
content="Hello world c!",
live=False,
),
],
}
for child_page, grandchild_pages in self.grandchildren_pages.items():
for grandchild_page in grandchild_pages:
child_page.add_child(instance=grandchild_page)
for child_page, grandchild_pages in self.grandchildren_pages.items():
for grandchild_page in grandchild_pages:
grandchild_page.content = grandchild_page.content.replace(
"Hello world", "Hello grandchild"
)
grandchild_page.save_revision()
# Add an additional grandchild page which will be bulk published from
# a draft-only state.
draft_page = SimplePage(
title="Hello world!-d",
slug="hello-world-d",
content="Hello grandchild d!",
live=False,
)
self.pages_to_be_published[1].add_child(instance=draft_page)
self.grandchildren_pages[self.pages_to_be_published[1]].append(draft_page)
self.url = (
reverse(
"wagtail_bulk_action",
args=(
"wagtailcore",
"page",
"publish",
),
)
+ "?"
)
for child_page in self.pages_to_be_published:
self.url += f"&id={child_page.id}"
self.user = self.login()
def test_publish_descendants_view(self):
"""
This tests that the publish view responds with a publish confirm page that contains the form field 'include_descendants'
"""
# Get publish page
response = self.client.get(self.url)
# Check that the user received a publish confirm page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(
response, "wagtailadmin/pages/bulk_actions/confirm_bulk_publish.html"
)
# Check the form contains the checkbox field include_descendants
self.assertContains(
response,
'name="include_descendants"',
)
def test_publish_include_children_view_post(self):
"""
This posts to the publish view and checks that the page and its descendants were published
"""
# Post to the publish page
response = self.client.post(self.url, {"include_descendants": "on"})
# Should be redirected to explorer page
self.assertEqual(response.status_code, 302)
# Check that the child pages were published
for child_page in self.pages_to_be_published:
published_child_page = SimplePage.objects.get(id=child_page.id)
self.assertTrue(published_child_page.live)
self.assertIn("Hello updated", published_child_page.content)
# Check that the child pages not to be published remain
for child_page in self.pages_not_to_be_published:
self.assertFalse(Page.objects.get(id=child_page.id).live)
for grandchild_pages in self.grandchildren_pages.values():
for grandchild_page in grandchild_pages:
published_grandchild_page = SimplePage.objects.get(
id=grandchild_page.id
)
self.assertTrue(published_grandchild_page.live)
self.assertIn("Hello grandchild", published_grandchild_page.content)
def test_publish_not_include_children_view_post(self):
"""
This posts to the publish view and checks that the page was published but its descendants were not
"""
# Post to the publish page
response = self.client.post(self.url, {})
# Should be redirected to explorer page
self.assertEqual(response.status_code, 302)
# Check that the child pages were published
for child_page in self.pages_to_be_published:
published_child_page = SimplePage.objects.get(id=child_page.id)
self.assertTrue(published_child_page.live)
self.assertIn("Hello updated", published_child_page.content)
# Check that the descendant pages were not published
for grandchild_pages in self.grandchildren_pages.values():
for grandchild_page in grandchild_pages:
self.assertFalse(Page.objects.get(id=grandchild_page.id).live)

View File

@@ -0,0 +1,312 @@
from unittest import mock
from django.contrib.auth.models import Permission
from django.http import HttpRequest, HttpResponse
from django.test import TestCase
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from wagtail.admin.views.pages.bulk_actions.page_bulk_action import PageBulkAction
from wagtail.models import Page
from wagtail.signals import page_unpublished
from wagtail.test.testapp.models import SimplePage
from wagtail.test.utils import WagtailTestUtils
class TestBulkUnpublish(WagtailTestUtils, TestCase):
def setUp(self):
# Create pages to unpublish
self.root_page = Page.objects.get(id=2)
self.child_pages = [
SimplePage(
title=f"Hello world!-{i}", slug=f"hello-world-{i}", content=f"hello-{i}"
)
for i in range(1, 5)
]
# first three child pages will be unpublished
self.pages_to_be_unpublished = self.child_pages[:3]
self.pages_not_to_be_unpublished = self.child_pages[3:]
for child_page in self.child_pages:
self.root_page.add_child(instance=child_page)
self.url = (
reverse(
"wagtail_bulk_action",
args=(
"wagtailcore",
"page",
"unpublish",
),
)
+ "?"
)
for child_page in self.pages_to_be_unpublished:
self.url += f"&id={child_page.id}"
self.redirect_url = reverse("wagtailadmin_explore", args=(self.root_page.id,))
# Login
self.user = self.login()
def test_unpublish_view(self):
"""
This tests that the unpublish view responds with an unpublish confirm page
"""
# Request confirm unpublish page
response = self.client.get(self.url)
# Check that the user received an unpublish confirm page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(
response, "wagtailadmin/pages/bulk_actions/confirm_bulk_unpublish.html"
)
def test_unpublish_view_invalid_page_id(self):
"""
This tests that the unpublish view returns an error if the page id is invalid
"""
# Request confirm unpublish page but with illegal page id
response = self.client.get(
reverse(
"wagtail_bulk_action",
args=(
"wagtailcore",
"page",
"unpublish",
),
)
)
# Check that the user received a 404 response
self.assertEqual(response.status_code, 404)
def test_unpublish_view_bad_permissions(self):
"""
This tests that the unpublish view doesn't allow users without unpublish permissions
"""
# 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()
# Request confirm unpublish page
response = self.client.get(self.url)
# Check that the user received a 200 redirected response
self.assertEqual(response.status_code, 200)
html = response.content.decode()
self.assertInHTML(
"<p>You don't have permission to unpublish these pages</p>", html
)
for child_page in self.pages_to_be_unpublished:
self.assertInHTML(f"<li>{child_page.title}</li>", html)
def test_unpublish_view_post(self):
"""
This posts to the unpublish view and checks that the page was unpublished
"""
# Connect a mock signal handler to page_unpublished signal
mock_handler = mock.MagicMock()
page_unpublished.connect(mock_handler)
try:
# Post to the unpublish page
response = self.client.post(self.url)
# Should be redirected to explorer page
self.assertEqual(response.status_code, 302)
# Check that the child pages were unpublished
for child_page in self.pages_to_be_unpublished:
self.assertFalse(SimplePage.objects.get(id=child_page.id).live)
# Check that the child pages not to be unpublished remain
for child_page in self.pages_not_to_be_unpublished:
self.assertTrue(SimplePage.objects.get(id=child_page.id).live)
# Check that the page_unpublished signal was fired
self.assertEqual(mock_handler.call_count, len(self.pages_to_be_unpublished))
for i, child_page in enumerate(self.pages_to_be_unpublished):
mock_call = mock_handler.mock_calls[i][2]
self.assertEqual(mock_call["sender"], child_page.specific_class)
self.assertEqual(mock_call["instance"], child_page)
self.assertIsInstance(mock_call["instance"], child_page.specific_class)
finally:
page_unpublished.disconnect(mock_handler)
def test_after_unpublish_page(self):
def hook_func(request, action_type, pages, action_class_instance):
self.assertEqual(action_type, "unpublish")
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(action_class_instance, PageBulkAction)
for i, page in enumerate(pages):
self.assertEqual(page.id, self.pages_to_be_unpublished[i].id)
return HttpResponse("Overridden!")
with self.register_hook("after_bulk_action", hook_func):
response = self.client.post(self.url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
for child_page in self.pages_to_be_unpublished:
child_page.refresh_from_db()
self.assertEqual(child_page.status_string, _("draft"))
def test_before_unpublish_page(self):
def hook_func(request, action_type, pages, action_class_instance):
self.assertEqual(action_type, "unpublish")
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(action_class_instance, PageBulkAction)
for i, page in enumerate(pages):
self.assertEqual(page.id, self.pages_to_be_unpublished[i].id)
return HttpResponse("Overridden!")
with self.register_hook("before_bulk_action", hook_func):
response = self.client.post(self.url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
def test_unpublish_descendants_view(self):
"""
This tests that the unpublish view responds with an unpublish confirm page that does not contain the form field 'include_descendants'
"""
# Get unpublish page for page with no descendants
response = self.client.get(self.url)
# Check that the user received an unpublish confirm page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(
response, "wagtailadmin/pages/bulk_actions/confirm_bulk_unpublish.html"
)
# Check the form does not contain the checkbox field include_descendants
self.assertContains(
response,
'name="include_descendants"',
count=0,
)
class TestBulkUnpublishIncludingDescendants(WagtailTestUtils, TestCase):
def setUp(self):
# Find root page
self.root_page = Page.objects.get(id=2)
self.child_pages = [
SimplePage(
title=f"Hello world!-{i}", slug=f"hello-world-{i}", content=f"hello-{i}"
)
for i in range(1, 5)
]
# first three child pages will be unpublished
self.pages_to_be_unpublished = self.child_pages[:3]
self.pages_not_to_be_unpublished = self.child_pages[3:]
for child_page in self.child_pages:
self.root_page.add_child(instance=child_page)
# map of the form { page: [child_pages] } to be added
self.grandchildren_pages = {
self.pages_to_be_unpublished[0]: [
SimplePage(
title="Hello world!-a", slug="hello-world-a", content="hello-a"
)
],
self.pages_to_be_unpublished[1]: [
SimplePage(
title="Hello world!-b", slug="hello-world-b", content="hello-b"
),
SimplePage(
title="Hello world!-c", slug="hello-world-c", content="hello-c"
),
],
}
for child_page, grandchild_pages in self.grandchildren_pages.items():
for grandchild_page in grandchild_pages:
child_page.add_child(instance=grandchild_page)
self.url = (
reverse(
"wagtail_bulk_action",
args=(
"wagtailcore",
"page",
"unpublish",
),
)
+ "?"
)
for child_page in self.pages_to_be_unpublished:
self.url += f"&id={child_page.id}"
self.redirect_url = reverse("wagtailadmin_explore", args=(self.root_page.id,))
self.user = self.login()
def test_unpublish_descendants_view(self):
"""
This tests that the unpublish view responds with an unpublish confirm page that contains the form field 'include_descendants'
"""
# Get unpublish page
response = self.client.get(self.url)
# Check that the user received an unpublish confirm page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(
response, "wagtailadmin/pages/bulk_actions/confirm_bulk_unpublish.html"
)
# Check the form contains the checkbox field include_descendants
self.assertContains(
response,
'name="include_descendants"',
)
def test_unpublish_include_children_view_post(self):
"""
This posts to the unpublish view and checks that the page and its descendants were unpublished
"""
# Post to the unpublish page
response = self.client.post(self.url, {"include_descendants": "on"})
# Should be redirected to explorer page
self.assertEqual(response.status_code, 302)
# Check that the child pages were unpublished
for child_page in self.pages_to_be_unpublished:
self.assertFalse(SimplePage.objects.get(id=child_page.id).live)
# Check that the child pages not to be unpublished remain
for child_page in self.pages_not_to_be_unpublished:
self.assertTrue(SimplePage.objects.get(id=child_page.id).live)
for grandchild_pages in self.grandchildren_pages.values():
for grandchild_page in grandchild_pages:
self.assertFalse(SimplePage.objects.get(id=grandchild_page.id).live)
def test_unpublish_not_include_children_view_post(self):
"""
This posts to the unpublish view and checks that the page was unpublished but its descendants were not
"""
# Post to the unpublish page
response = self.client.post(self.url, {})
# Should be redirected to explorer page
self.assertEqual(response.status_code, 302)
# Check that the child pages were unpublished
for child_page in self.pages_to_be_unpublished:
self.assertFalse(SimplePage.objects.get(id=child_page.id).live)
# Check that the descendant pages were not unpublished
for grandchild_pages in self.grandchildren_pages.values():
for grandchild_page in grandchild_pages:
self.assertTrue(SimplePage.objects.get(id=grandchild_page.id).live)

View File

@@ -0,0 +1,36 @@
from django.test import TestCase
from django.urls import reverse
from django.utils.http import urlencode
from wagtail.test.testapp.models import EventPage
from wagtail.test.utils import WagtailTestUtils
class TestContentTypeUse(WagtailTestUtils, TestCase):
fixtures = ["test.json"]
def setUp(self):
self.user = self.login()
self.christmas_page = EventPage.objects.get(title="Christmas")
def test_content_type_use(self):
# Get use of event page
request_url = reverse(
"wagtailadmin_pages:type_use", args=("tests", "eventpage")
)
response = self.client.get(request_url)
# Check response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "wagtailadmin/generic/listing.html")
self.assertTemplateUsed(response, "wagtailadmin/pages/usage_results.html")
self.assertContains(response, "Christmas")
# Links to 'delete' etc should include a 'next' URL parameter pointing back here
delete_url = (
reverse("wagtailadmin_pages:delete", args=(self.christmas_page.id,))
+ "?"
+ urlencode({"next": request_url})
)
self.assertContains(response, delete_url)
self.assertNotContains(response, "data-bulk-action-select-all-checkbox")

View File

@@ -0,0 +1,89 @@
from django.contrib.auth.models import Permission
from django.test import TestCase
from django.urls import reverse
from wagtail.models import Page, PageLogEntry
from wagtail.test.testapp.models import SimplePage
from wagtail.test.utils import WagtailTestUtils
class TestConvertAlias(WagtailTestUtils, TestCase):
def setUp(self):
# Find root page
self.root_page = Page.objects.get(id=2)
# Add child page
self.child_page = SimplePage(
title="Hello world!", slug="hello-world", content="hello"
)
self.root_page.add_child(instance=self.child_page)
# Add alias page
self.alias_page = self.child_page.create_alias(update_slug="alias-page")
# Login
self.user = self.login()
def test_convert_alias(self):
response = self.client.get(
reverse("wagtailadmin_pages:convert_alias", args=[self.alias_page.id])
)
self.assertEqual(response.status_code, 200)
def test_convert_alias_not_alias(self):
response = self.client.get(
reverse("wagtailadmin_pages:convert_alias", args=[self.child_page.id])
)
self.assertEqual(response.status_code, 404)
def test_convert_alias_bad_permission(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(
reverse("wagtailadmin_pages:convert_alias", args=[self.alias_page.id])
)
# Check that the user received a permission denied response
self.assertRedirects(response, "/admin/")
def test_post_convert_alias(self):
response = self.client.post(
reverse("wagtailadmin_pages:convert_alias", args=[self.alias_page.id])
)
# User should be redirected to the edit view of the converted page
self.assertRedirects(
response, reverse("wagtailadmin_pages:edit", args=[self.alias_page.id])
)
# Check the page was converted
self.alias_page.refresh_from_db()
self.assertIsNone(self.alias_page.alias_of)
# Check that a revision was created
revision = self.alias_page.revisions.get()
self.assertEqual(revision.user, self.user)
self.assertEqual(self.alias_page.live_revision, revision)
# Check audit log
log = PageLogEntry.objects.get(action="wagtail.convert_alias")
self.assertFalse(log.content_changed)
self.assertEqual(
log.data,
{
"page": {
"id": self.alias_page.id,
"title": self.alias_page.get_admin_display_title(),
}
},
)
self.assertEqual(log.page, self.alias_page.page_ptr)
self.assertEqual(log.revision, revision)
self.assertEqual(log.user, self.user)

View File

@@ -0,0 +1,800 @@
from django.contrib.auth.models import Group, Permission
from django.http import HttpRequest, HttpResponse
from django.test import TestCase
from django.urls import reverse
from wagtail.models import GroupPagePermission, Page
from wagtail.test.testapp.models import (
CustomCopyFormPage,
EventPage,
EventPageSpeaker,
PageWithExcludedCopyField,
SimplePage,
)
from wagtail.test.utils import WagtailTestUtils
class TestPageCopy(WagtailTestUtils, TestCase):
def setUp(self):
# Find root page
self.root_page = Page.objects.get(id=2)
# Create a page
self.test_page = self.root_page.add_child(
instance=SimplePage(
title="Hello world!",
slug="hello-world",
content="hello",
live=True,
has_unpublished_changes=False,
)
)
# Create a couple of child pages
self.test_child_page = self.test_page.add_child(
instance=SimplePage(
title="Child page",
slug="child-page",
content="hello",
live=True,
has_unpublished_changes=True,
)
)
self.test_unpublished_child_page = self.test_page.add_child(
instance=SimplePage(
title="Unpublished Child page",
slug="unpublished-child-page",
content="hello",
live=False,
has_unpublished_changes=True,
)
)
# Login
self.user = self.login()
def test_page_copy(self):
response = self.client.get(
reverse("wagtailadmin_pages:copy", args=(self.test_page.id,))
)
# Check response
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "wagtailadmin/pages/copy.html")
# Make sure all fields are in the form
self.assertContains(response, "New title")
self.assertContains(response, "New slug")
self.assertContains(response, "New parent page")
self.assertContains(response, "Copy subpages")
self.assertContains(response, "Publish copies")
self.assertContains(response, "Alias")
def test_page_copy_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()
# Get copy page
post_data = {
"new_title": "Hello world 2",
"new_slug": "hello-world",
"new_parent_page": str(self.test_page.id),
"copy_subpages": False,
"alias": False,
}
response = self.client.post(
reverse("wagtailadmin_pages:copy", args=(self.test_page.id,)), post_data
)
# A user with no page permissions at all should be redirected to the admin home
self.assertRedirects(response, reverse("wagtailadmin_home"))
# A user with page permissions, but not add permission at the destination,
# should receive a form validation error
publishers = Group.objects.create(name="Publishers")
GroupPagePermission.objects.create(
group=publishers, page=self.root_page, permission_type="publish"
)
self.user.groups.add(publishers)
self.user.save()
# Get copy page
post_data = {
"new_title": "Hello world 2",
"new_slug": "hello-world",
"new_parent_page": str(self.test_page.id),
"copy_subpages": False,
"alias": False,
}
response = self.client.post(
reverse("wagtailadmin_pages:copy", args=(self.test_page.id,)), post_data
)
form = response.context["form"]
self.assertFalse(form.is_valid())
self.assertIn("new_parent_page", form.errors)
def test_page_copy_post(self):
post_data = {
"new_title": "Hello world 2",
"new_slug": "hello-world-2",
"new_parent_page": str(self.root_page.id),
"copy_subpages": False,
"publish_copies": False,
"alias": False,
}
response = self.client.post(
reverse("wagtailadmin_pages:copy", args=(self.test_page.id,)), post_data
)
# Check that the user was redirected to the parents explore page
self.assertRedirects(
response, reverse("wagtailadmin_explore", args=(self.root_page.id,))
)
# Get copy
page_copy = self.root_page.get_children().filter(slug="hello-world-2").first()
# Check that the copy exists
self.assertIsNotNone(page_copy)
# Check that the copy is not live
self.assertFalse(page_copy.live)
self.assertTrue(page_copy.has_unpublished_changes)
# Check that the owner of the page is set correctly
self.assertEqual(page_copy.owner, self.user)
# Check that the children were not copied
self.assertEqual(page_copy.get_children().count(), 0)
# treebeard should report no consistency problems with the tree
self.assertFalse(
any(Page.find_problems()), msg="treebeard found consistency problems"
)
def test_page_with_exclude_fields_in_copy(self):
original_page = self.test_page.add_child(
instance=PageWithExcludedCopyField(
title="Page with exclude_fields_in_copy",
slug="page-with-exclude-fields-in-copy",
content="Copy me",
special_field="Don't copy me",
live=True,
has_unpublished_changes=False,
)
)
post_data = {
"new_title": f"{original_page.title} 2",
"new_slug": f"{original_page.slug}-2",
"new_parent_page": str(self.root_page.id),
"copy_subpages": False,
"publish_copies": False,
"alias": False,
}
self.client.post(
reverse("wagtailadmin_pages:copy", args=(original_page.id,)), post_data
)
# Get copy
page_copy = PageWithExcludedCopyField.objects.get(slug=post_data["new_slug"])
self.assertEqual(page_copy.content, original_page.content)
self.assertNotEqual(page_copy.special_field, original_page.special_field)
self.assertEqual(
page_copy.special_field, page_copy._meta.get_field("special_field").default
)
def test_page_copy_post_copy_subpages(self):
post_data = {
"new_title": "Hello world 2",
"new_slug": "hello-world-2",
"new_parent_page": str(self.root_page.id),
"copy_subpages": True,
"publish_copies": False,
"alias": False,
}
response = self.client.post(
reverse("wagtailadmin_pages:copy", args=(self.test_page.id,)), post_data
)
# Check that the user was redirected to the parents explore page
self.assertRedirects(
response, reverse("wagtailadmin_explore", args=(self.root_page.id,))
)
# Get copy
page_copy = self.root_page.get_children().filter(slug="hello-world-2").first()
# Check that the copy exists
self.assertIsNotNone(page_copy)
# Check that the copy is not live
self.assertFalse(page_copy.live)
self.assertTrue(page_copy.has_unpublished_changes)
# Check that the owner of the page is set correctly
self.assertEqual(page_copy.owner, self.user)
# Check that the children were copied
self.assertEqual(page_copy.get_children().count(), 2)
# Check the child pages
# Neither of them should be live
child_copy = page_copy.get_children().filter(slug="child-page").first()
self.assertIsNotNone(child_copy)
self.assertFalse(child_copy.live)
self.assertTrue(child_copy.has_unpublished_changes)
unpublished_child_copy = (
page_copy.get_children().filter(slug="unpublished-child-page").first()
)
self.assertIsNotNone(unpublished_child_copy)
self.assertFalse(unpublished_child_copy.live)
self.assertTrue(unpublished_child_copy.has_unpublished_changes)
# treebeard should report no consistency problems with the tree
self.assertFalse(
any(Page.find_problems()), msg="treebeard found consistency problems"
)
def test_page_copy_post_copy_subpages_publish_copies(self):
post_data = {
"new_title": "Hello world 2",
"new_slug": "hello-world-2",
"new_parent_page": str(self.root_page.id),
"copy_subpages": True,
"publish_copies": True,
"alias": False,
}
response = self.client.post(
reverse("wagtailadmin_pages:copy", args=(self.test_page.id,)), post_data
)
# Check that the user was redirected to the parents explore page
self.assertRedirects(
response, reverse("wagtailadmin_explore", args=(self.root_page.id,))
)
# Get copy
page_copy = self.root_page.get_children().filter(slug="hello-world-2").first()
# Check that the copy exists
self.assertIsNotNone(page_copy)
# Check that the copy is live
self.assertTrue(page_copy.live)
self.assertFalse(page_copy.has_unpublished_changes)
# Check that the owner of the page is set correctly
self.assertEqual(page_copy.owner, self.user)
# Check that the children were copied
self.assertEqual(page_copy.get_children().count(), 2)
# Check the child pages
# The child_copy should be live but the unpublished_child_copy shouldn't
child_copy = page_copy.get_children().filter(slug="child-page").first()
self.assertIsNotNone(child_copy)
self.assertTrue(child_copy.live)
self.assertTrue(child_copy.has_unpublished_changes)
unpublished_child_copy = (
page_copy.get_children().filter(slug="unpublished-child-page").first()
)
self.assertIsNotNone(unpublished_child_copy)
self.assertFalse(unpublished_child_copy.live)
self.assertTrue(unpublished_child_copy.has_unpublished_changes)
# treebeard should report no consistency problems with the tree
self.assertFalse(
any(Page.find_problems()), msg="treebeard found consistency problems"
)
def test_page_copy_post_new_parent(self):
post_data = {
"new_title": "Hello world 2",
"new_slug": "hello-world-2",
"new_parent_page": str(self.test_child_page.id),
"copy_subpages": False,
"publish_copies": False,
"alias": False,
}
response = self.client.post(
reverse("wagtailadmin_pages:copy", args=(self.test_page.id,)), post_data
)
# Check that the user was redirected to the new parents explore page
self.assertRedirects(
response, reverse("wagtailadmin_explore", args=(self.test_child_page.id,))
)
# Check that the page was copied to the correct place
self.assertTrue(
Page.objects.filter(slug="hello-world-2").first().get_parent(),
msg=self.test_child_page,
)
# treebeard should report no consistency problems with the tree
self.assertFalse(
any(Page.find_problems()), msg="treebeard found consistency problems"
)
def test_page_copy_post_existing_slug_within_same_parent_page(self):
# This tests the existing slug checking on page copy when not changing the parent page
# Attempt to copy the page but forget to change the slug
post_data = {
"new_title": "Hello world 2",
"new_slug": "hello-world",
"new_parent_page": str(self.root_page.id),
"copy_subpages": False,
"alias": False,
}
response = self.client.post(
reverse("wagtailadmin_pages:copy", args=(self.test_page.id,)), post_data
)
# Should not be redirected (as the save should fail)
self.assertEqual(response.status_code, 200)
# Check that a form error was raised
self.assertFormError(
response.context["form"],
"new_slug",
'This slug is already in use within the context of its parent page "Welcome to your new Wagtail site!"',
)
def test_page_copy_post_and_subpages_to_same_tree_branch(self):
# This tests that a page cannot be copied into itself when copying subpages
post_data = {
"new_title": "Hello world 2",
"new_slug": "hello-world",
"new_parent_page": str(self.test_child_page.id),
"copy_subpages": True,
"alias": False,
}
response = self.client.post(
reverse("wagtailadmin_pages:copy", args=(self.test_page.id,)), post_data
)
# Should not be redirected (as the save should fail)
self.assertEqual(response.status_code, 200)
# Check that a form error was raised
self.assertFormError(
response.context["form"],
"new_parent_page",
"You cannot copy a page into itself when copying subpages",
)
def test_page_copy_post_existing_slug_to_another_parent_page(self):
# This tests the existing slug checking on page copy when changing the parent page
# Attempt to copy the page and changed the parent page
post_data = {
"new_title": "Hello world 2",
"new_slug": "hello-world",
"new_parent_page": str(self.test_child_page.id),
"copy_subpages": False,
"alias": False,
}
response = self.client.post(
reverse("wagtailadmin_pages:copy", args=(self.test_page.id,)), post_data
)
# Check that the user was redirected to the parents explore page
self.assertRedirects(
response, reverse("wagtailadmin_explore", args=(self.test_child_page.id,))
)
def test_page_copy_post_invalid_slug(self):
# Attempt to copy the page but set an invalid slug string
post_data = {
"new_title": "Hello world 2",
"new_slug": "hello world!",
"new_parent_page": str(self.root_page.id),
"copy_subpages": False,
"alias": False,
}
response = self.client.post(
reverse("wagtailadmin_pages:copy", args=(self.test_page.id,)), post_data
)
# Should not be redirected (as the save should fail)
self.assertEqual(response.status_code, 200)
# Check that a form error was raised
self.assertFormError(
response.context["form"],
"new_slug",
"Enter a valid “slug” consisting of Unicode letters, numbers, underscores, or hyphens.",
)
def test_page_copy_post_valid_unicode_slug(self):
post_data = {
"new_title": "Hello wɜːld",
"new_slug": "hello-wɜːld",
"new_parent_page": str(self.test_page.id),
"copy_subpages": False,
"alias": False,
}
response = self.client.post(
reverse("wagtailadmin_pages:copy", args=(self.test_page.id,)), post_data
)
# Check response
self.assertRedirects(
response, reverse("wagtailadmin_explore", args=(self.test_page.id,))
)
# Get copy
page_copy = (
self.test_page.get_children().filter(slug=post_data["new_slug"]).first()
)
# Check that the copy exists with the good slug
self.assertIsNotNone(page_copy)
self.assertEqual(page_copy.slug, post_data["new_slug"])
def test_page_copy_no_publish_permission(self):
# Turn user into an editor who can add pages but not publish them
self.user.is_superuser = False
self.user.groups.add(
Group.objects.get(name="Editors"),
)
self.user.save()
# Get copy page
response = self.client.get(
reverse("wagtailadmin_pages:copy", args=(self.test_page.id,))
)
# The user should have access to the copy page
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "wagtailadmin/pages/copy.html")
# Make sure the "publish copies" field is hidden
self.assertNotContains(response, "Publish copies")
def test_page_copy_no_publish_permission_post_copy_subpages_publish_copies(self):
# This tests that unprivileged users cannot publish copied pages even if they hack their browser
# Turn user into an editor who can add pages but not publish them
self.user.is_superuser = False
self.user.groups.add(
Group.objects.get(name="Editors"),
)
self.user.save()
# Post
post_data = {
"new_title": "Hello world 2",
"new_slug": "hello-world-2",
"new_parent_page": str(self.root_page.id),
"copy_subpages": True,
"publish_copies": True,
"alias": False,
}
response = self.client.post(
reverse("wagtailadmin_pages:copy", args=(self.test_page.id,)), post_data
)
# Check that the user was redirected to the parents explore page
self.assertRedirects(
response, reverse("wagtailadmin_explore", args=(self.root_page.id,))
)
# Get copy
page_copy = self.root_page.get_children().filter(slug="hello-world-2").first()
# Check that the copy exists
self.assertIsNotNone(page_copy)
# Check that the copy is not live
self.assertFalse(page_copy.live)
# Check that the owner of the page is set correctly
self.assertEqual(page_copy.owner, self.user)
# Check that the children were copied
self.assertEqual(page_copy.get_children().count(), 2)
# Check the child pages
# Neither of them should be live
child_copy = page_copy.get_children().filter(slug="child-page").first()
self.assertIsNotNone(child_copy)
self.assertFalse(child_copy.live)
unpublished_child_copy = (
page_copy.get_children().filter(slug="unpublished-child-page").first()
)
self.assertIsNotNone(unpublished_child_copy)
self.assertFalse(unpublished_child_copy.live)
# treebeard should report no consistency problems with the tree
self.assertFalse(
any(Page.find_problems()), msg="treebeard found consistency problems"
)
def test_before_copy_page_hook(self):
def hook_func(request, page):
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(page.specific, SimplePage)
return HttpResponse("Overridden!")
with self.register_hook("before_copy_page", hook_func):
response = self.client.get(
reverse("wagtailadmin_pages:copy", args=(self.test_page.id,))
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
def test_before_copy_page_hook_post(self):
def hook_func(request, page):
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(page.specific, SimplePage)
return HttpResponse("Overridden!")
with self.register_hook("before_copy_page", hook_func):
post_data = {
"new_title": "Hello world 2",
"new_slug": "hello-world-2",
"new_parent_page": str(self.root_page.id),
"copy_subpages": False,
"publish_copies": False,
"alias": False,
}
response = self.client.post(
reverse("wagtailadmin_pages:copy", args=(self.test_page.id,)), post_data
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# page should not be copied
self.assertFalse(Page.objects.filter(title="Hello world 2").exists())
def test_after_copy_page_hook(self):
def hook_func(request, page, new_page):
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(page.specific, SimplePage)
self.assertIsInstance(new_page.specific, SimplePage)
return HttpResponse("Overridden!")
with self.register_hook("after_copy_page", hook_func):
post_data = {
"new_title": "Hello world 2",
"new_slug": "hello-world-2",
"new_parent_page": str(self.root_page.id),
"copy_subpages": False,
"publish_copies": False,
"alias": False,
}
response = self.client.post(
reverse("wagtailadmin_pages:copy", args=(self.test_page.id,)), post_data
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# page should be copied
self.assertTrue(Page.objects.filter(title="Hello world 2").exists())
def test_page_copy_alias_post(self):
post_data = {
"new_title": "Hello world 2",
"new_slug": "hello-world-2",
"new_parent_page": str(self.root_page.id),
"copy_subpages": False,
"publish_copies": False,
"alias": True,
}
response = self.client.post(
reverse("wagtailadmin_pages:copy", args=(self.test_page.id,)), post_data
)
# Check that the user was redirected to the parents explore page
self.assertRedirects(
response, reverse("wagtailadmin_explore", args=(self.root_page.id,))
)
# Get copy
page_copy = self.root_page.get_children().get(slug="hello-world-2")
# Check the copy is an alias of the original
self.assertEqual(page_copy.alias_of, self.test_page.page_ptr)
# Check that the copy is live
# Note: publish_copies is ignored. Alias pages always keep the same state as their original
self.assertTrue(page_copy.live)
self.assertFalse(page_copy.has_unpublished_changes)
# Check that the owner of the page is set correctly
self.assertEqual(page_copy.owner, self.user)
# Check that the children were not copied
self.assertEqual(page_copy.get_children().count(), 0)
# treebeard should report no consistency problems with the tree
self.assertFalse(
any(Page.find_problems()), msg="treebeard found consistency problems"
)
def test_page_copy_alias_post_copy_subpages(self):
post_data = {
"new_title": "Hello world 2",
"new_slug": "hello-world-2",
"new_parent_page": str(self.root_page.id),
"copy_subpages": True,
"publish_copies": False,
"alias": True,
}
response = self.client.post(
reverse("wagtailadmin_pages:copy", args=(self.test_page.id,)), post_data
)
# Check that the user was redirected to the parents explore page
self.assertRedirects(
response, reverse("wagtailadmin_explore", args=(self.root_page.id,))
)
# Get copy
page_copy = self.root_page.get_children().get(slug="hello-world-2")
# Check the copy is an alias of the original
self.assertEqual(page_copy.alias_of, self.test_page.page_ptr)
# Check that the copy is live
# Note: publish_copies is ignored. Alias pages always keep the same state as their original
self.assertTrue(page_copy.live)
self.assertFalse(page_copy.has_unpublished_changes)
# Check that the owner of the page is set correctly
self.assertEqual(page_copy.owner, self.user)
# Check that the children were copied
self.assertEqual(page_copy.get_children().count(), 2)
# Check the child pages
# Neither of them should be live
child_copy = page_copy.get_children().filter(slug="child-page").first()
self.assertIsNotNone(child_copy)
self.assertEqual(child_copy.alias_of, self.test_child_page.page_ptr)
self.assertTrue(child_copy.live)
self.assertFalse(child_copy.has_unpublished_changes)
unpublished_child_copy = (
page_copy.get_children().filter(slug="unpublished-child-page").first()
)
self.assertIsNotNone(unpublished_child_copy)
self.assertEqual(
unpublished_child_copy.alias_of, self.test_unpublished_child_page.page_ptr
)
self.assertFalse(unpublished_child_copy.live)
self.assertTrue(unpublished_child_copy.has_unpublished_changes)
# treebeard should report no consistency problems with the tree
self.assertFalse(
any(Page.find_problems()), msg="treebeard found consistency problems"
)
def test_page_copy_alias_post_without_source_publish_permission(self):
# Check for issue #7293 - If the user has permission to publish at a destination, but not the source.
# Wagtail would crash on attempt to copy
# Create a new section
self.destination_page = self.root_page.add_child(
instance=SimplePage(
title="Destination page",
slug="destination-page",
content="hello",
live=True,
has_unpublished_changes=False,
)
)
# Make user a moderator and make it so they can only publish at the destination page
self.user.is_superuser = False
self.user.groups.add(Group.objects.get(name="Moderators"))
self.user.save()
GroupPagePermission.objects.filter(permission__codename="publish_page").update(
page=self.destination_page
)
post_data = {
"new_title": self.test_child_page.title,
"new_slug": self.test_child_page.slug,
"new_parent_page": str(self.destination_page.id),
"copy_subpages": False,
"publish_copies": False,
"alias": False,
}
response = self.client.post(
reverse("wagtailadmin_pages:copy", args=[self.test_child_page.id]),
post_data,
)
# We only need to check that it didn't crash
self.assertEqual(response.status_code, 302)
def test_copy_page_with_unique_uuids_in_orderables(self):
"""
Test that a page with orderables can be copied and the translation
keys are updated.
"""
event_page = EventPage(
title="Moon Landing",
location="the moon",
audience="public",
cost="free on TV",
date_from="1969-07-20",
)
self.root_page.add_child(instance=event_page)
event_page.speakers.add(
EventPageSpeaker(
first_name="Neil",
last_name="Armstrong",
)
)
# ensure there's a revision (which should capture the new speaker orderables)
# before copying the page
event_page.save_revision().publish()
post_data = {
"new_title": "New Moon landing",
"new_slug": "moon-landing-redux",
"new_parent_page": str(self.root_page.id),
"copy_subpages": False,
"publish_copies": False,
"alias": False,
}
self.client.post(
reverse("wagtailadmin_pages:copy", args=[event_page.id]), post_data
)
new_page = EventPage.objects.last()
# Hack: get the page instance from the edit form which assembles it from the
# latest revision. While we could do new_page.get_latest_revision().as_object()
# this follow the edit view closer and should it change the test is less
# prone to continue working because we're skipping some step
response = self.client.get(
reverse("wagtailadmin_pages:edit", args=[new_page.id])
)
new_page_on_edit_form = response.context["form"].instance
# publish the page, similar to EditView.publish_action()
new_page_on_edit_form.save_revision().publish()
self.assertNotEqual(
event_page.speakers.first().translation_key,
new_page.speakers.first().translation_key,
)
def test_page_copy_with_custom_copy_form(self):
custom_copy_form_page = self.root_page.add_child(
instance=CustomCopyFormPage(
title="Hello world!",
slug="copy-form",
live=True,
has_unpublished_changes=False,
)
)
response = self.client.get(
reverse("wagtailadmin_pages:copy", args=(custom_copy_form_page.id,))
)
# Check response
self.assertEqual(response.status_code, 200)
# Check if slug is hello-world-2
self.assertContains(response, "copy-form-2")

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,37 @@
from django.test import TestCase
from wagtail.test.utils import WagtailTestUtils
from wagtail.test.utils.template_tests import AdminTemplateTestUtils
class TestCustomListing(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
fixtures = ["test.json"]
def test_get(self):
self.login()
response = self.client.get("/admin/event_pages/")
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "wagtailadmin/pages/index.html")
self.assertContains(response, "Event pages")
self.assertContains(response, "Christmas")
self.assertContains(response, "Saint Patrick")
self.assertNotContains(response, "Welcome to the Wagtail test site!")
self.assertBreadcrumbsItemsRendered(
[{"url": "", "label": "Event pages"}],
response.content,
)
soup = self.get_soup(response.content)
breadcrumbs_icon = soup.select_one(".w-breadcrumbs__icon")
self.assertIsNotNone(breadcrumbs_icon)
use = breadcrumbs_icon.select_one("use")
self.assertIsNotNone(use)
self.assertEqual(use["href"], "#icon-calendar")
def test_filter(self):
self.login()
response = self.client.get("/admin/event_pages/", {"audience": "private"})
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "wagtailadmin/pages/index.html")
self.assertContains(response, "Event pages")
self.assertNotContains(response, "Christmas")
self.assertContains(response, "Saint Patrick")

View File

@@ -0,0 +1,366 @@
from unittest import mock
from django.contrib.auth.models import Permission
from django.db.models.signals import post_delete, pre_delete
from django.http import HttpRequest, HttpResponse
from django.test import TestCase, override_settings
from django.urls import reverse
from wagtail.models import Page
from wagtail.signals import page_unpublished
from wagtail.test.testapp.models import SimplePage, StandardChild, StandardIndex
from wagtail.test.utils import WagtailTestUtils
class TestPageDelete(WagtailTestUtils, TestCase):
def setUp(self):
# Find root page
self.root_page = Page.objects.get(id=2)
# Add child page
self.child_page = SimplePage(
title="Hello world!", slug="hello-world", content="hello"
)
self.root_page.add_child(instance=self.child_page)
# Add a page with child pages of its own
self.child_index = StandardIndex(title="Hello index", slug="hello-index")
self.root_page.add_child(instance=self.child_index)
self.grandchild_page = StandardChild(title="Hello Kitty", slug="hello-kitty")
self.child_index.add_child(instance=self.grandchild_page)
# Login
self.user = self.login()
def test_page_delete(self):
response = self.client.get(
reverse("wagtailadmin_pages:delete", args=(self.child_page.id,))
)
self.assertEqual(response.status_code, 200)
# deletion should not actually happen on GET
self.assertTrue(SimplePage.objects.filter(id=self.child_page.id).exists())
@override_settings(WAGTAILADMIN_UNSAFE_PAGE_DELETION_LIMIT=10)
def test_confirm_delete_scenario_1(self):
# If the number of pages to be deleted are less than
# WAGTAILADMIN_UNSAFE_PAGE_DELETION_LIMIT then don't need
# for confirmation
child_1 = SimplePage(title="child 1", slug="child-1", content="hello")
self.child_page.add_child(instance=child_1)
child_2 = SimplePage(title="child 2", slug="child-2", content="hello")
self.child_page.add_child(instance=child_2)
response = self.client.get(
reverse("wagtailadmin_pages:delete", args=(self.child_page.id,))
)
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, '<input type="text" name="confirm_site_name"')
# deletion should not actually happen on GET
self.assertTrue(SimplePage.objects.filter(id=self.child_page.id).exists())
# And admin should be able to delete page without any confirmation
self.client.post(
reverse("wagtailadmin_pages:delete", args=(self.child_page.id,))
)
# Check that page is deleted
self.assertFalse(SimplePage.objects.filter(id=self.child_page.id).exists())
@override_settings(WAGTAILADMIN_UNSAFE_PAGE_DELETION_LIMIT=3)
@override_settings(WAGTAIL_SITE_NAME="mysite")
def test_confirm_delete_scenario_2(self):
# If the number of pages to be deleted are greater than or equal to
# WAGTAILADMIN_UNSAFE_PAGE_DELETION_LIMIT then show input box
# to input wagtail_site_name.
child_1 = SimplePage(title="child 1", slug="child-1", content="hello")
self.child_page.add_child(instance=child_1)
child_2 = SimplePage(title="child 2", slug="child-2", content="hello")
self.child_page.add_child(instance=child_2)
response = self.client.get(
reverse("wagtailadmin_pages:delete", args=(self.child_page.id,))
)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "This action will delete total <b>3</b> pages.")
self.assertContains(response, "Please type <b>mysite</b> to confirm.")
self.assertContains(response, '<input type="text" name="confirm_site_name"')
# deletion should not actually happen on GET
self.assertTrue(SimplePage.objects.filter(id=self.child_page.id).exists())
@override_settings(WAGTAILADMIN_UNSAFE_PAGE_DELETION_LIMIT=3)
@override_settings(WAGTAIL_SITE_NAME="mysite")
def test_confirm_delete_scenario_3(self):
# If admin entered the incorrect site name and submit
# the form, then site should not be deleted and same
# form should be displayed again.
child_1 = SimplePage(title="child 1", slug="child-1", content="hello")
self.child_page.add_child(instance=child_1)
child_2 = SimplePage(title="child 2", slug="child-2", content="hello")
self.child_page.add_child(instance=child_2)
response = self.client.post(
reverse("wagtailadmin_pages:delete", args=(self.child_page.id,)),
data={"confirm_site_name": "random"},
)
self.assertEqual(response.status_code, 200)
# One error messages should be returned
messages = [m.message for m in response.context["messages"]]
self.assertEqual(len(messages), 1)
self.assertContains(response, "This action will delete total <b>3</b> pages.")
self.assertContains(response, "Please type <b>mysite</b> to confirm.")
self.assertContains(response, '<input type="text" name="confirm_site_name"')
# Site should not be deleted
self.assertTrue(SimplePage.objects.filter(id=self.child_page.id).exists())
@override_settings(WAGTAILADMIN_UNSAFE_PAGE_DELETION_LIMIT=3)
@override_settings(WAGTAIL_SITE_NAME="mysite")
def test_confirm_delete_scenario_4(self):
# If admin entered the correct site name and submit the form
# then the site should be deleted
child_1 = SimplePage(title="child 1", slug="child-1", content="hello")
self.child_page.add_child(instance=child_1)
child_2 = SimplePage(title="child 2", slug="child-2", content="hello")
self.child_page.add_child(instance=child_2)
response = self.client.post(
reverse("wagtailadmin_pages:delete", args=(self.child_page.id,)),
data={"confirm_site_name": "mysite"},
)
# Should be redirected to explorer page
self.assertRedirects(
response, reverse("wagtailadmin_explore", args=(self.root_page.id,))
)
# Check that the page is deleted
self.assertFalse(SimplePage.objects.filter(id=self.child_page.id).exists())
# Check that the subpage is also deleted
self.assertFalse(SimplePage.objects.filter(id=child_1.id).exists())
self.assertFalse(SimplePage.objects.filter(id=child_2.id).exists())
def test_page_delete_specific_admin_title(self):
response = self.client.get(
reverse("wagtailadmin_pages:delete", args=(self.child_page.id,))
)
self.assertEqual(response.status_code, 200)
# The admin_display_title specific to ChildPage is shown on the delete confirmation page.
self.assertContains(response, self.child_page.get_admin_display_title())
def test_page_delete_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()
# Get delete page
response = self.client.get(
reverse("wagtailadmin_pages:delete", args=(self.child_page.id,))
)
# Check that the user received a 302 redirect response
self.assertEqual(response.status_code, 302)
# Check that the deletion has not happened
self.assertTrue(SimplePage.objects.filter(id=self.child_page.id).exists())
def test_page_delete_post(self):
# Connect a mock signal handler to page_unpublished signal
mock_handler = mock.MagicMock()
page_unpublished.connect(mock_handler)
try:
# Post
response = self.client.post(
reverse("wagtailadmin_pages:delete", args=(self.child_page.id,))
)
# Should be redirected to explorer page
self.assertRedirects(
response, reverse("wagtailadmin_explore", args=(self.root_page.id,))
)
# treebeard should report no consistency problems with the tree
self.assertFalse(
any(Page.find_problems()), msg="treebeard found consistency problems"
)
# Check that the page is gone
self.assertEqual(
Page.objects.filter(
path__startswith=self.root_page.path, slug="hello-world"
).count(),
0,
)
# Check that the page_unpublished signal was fired
self.assertEqual(mock_handler.call_count, 1)
mock_call = mock_handler.mock_calls[0][2]
self.assertEqual(mock_call["sender"], self.child_page.specific_class)
self.assertEqual(mock_call["instance"], self.child_page)
self.assertIsInstance(mock_call["instance"], self.child_page.specific_class)
finally:
page_unpublished.disconnect(mock_handler)
def test_page_delete_notlive_post(self):
# Same as above, but this makes sure the page_unpublished signal is not fired
# when if the page is not live when it is deleted
# Unpublish the page
self.child_page.live = False
self.child_page.save()
# Connect a mock signal handler to page_unpublished signal
mock_handler = mock.MagicMock()
page_unpublished.connect(mock_handler)
try:
# Post
response = self.client.post(
reverse("wagtailadmin_pages:delete", args=(self.child_page.id,))
)
# Should be redirected to explorer page
self.assertRedirects(
response, reverse("wagtailadmin_explore", args=(self.root_page.id,))
)
# treebeard should report no consistency problems with the tree
self.assertFalse(
any(Page.find_problems()), msg="treebeard found consistency problems"
)
# Check that the page is gone
self.assertEqual(
Page.objects.filter(
path__startswith=self.root_page.path, slug="hello-world"
).count(),
0,
)
# Check that the page_unpublished signal was not fired
self.assertEqual(mock_handler.call_count, 0)
finally:
page_unpublished.disconnect(mock_handler)
def test_subpage_deletion(self):
# Connect mock signal handlers to page_unpublished, pre_delete and post_delete signals
unpublish_signals_received = []
pre_delete_signals_received = []
post_delete_signals_received = []
def page_unpublished_handler(sender, instance, **kwargs):
unpublish_signals_received.append((sender, instance.pk))
def pre_delete_handler(sender, instance, **kwargs):
pre_delete_signals_received.append((sender, instance.pk))
def post_delete_handler(sender, instance, **kwargs):
post_delete_signals_received.append((sender, instance.pk))
page_unpublished.connect(page_unpublished_handler)
pre_delete.connect(pre_delete_handler)
post_delete.connect(post_delete_handler)
try:
# Post
response = self.client.post(
reverse("wagtailadmin_pages:delete", args=(self.child_index.id,))
)
# Should be redirected to explorer page
self.assertRedirects(
response, reverse("wagtailadmin_explore", args=(self.root_page.id,))
)
# treebeard should report no consistency problems with the tree
self.assertFalse(
any(Page.find_problems()), msg="treebeard found consistency problems"
)
# Check that the page is gone
self.assertFalse(
StandardIndex.objects.filter(id=self.child_index.id).exists()
)
self.assertFalse(Page.objects.filter(id=self.child_index.id).exists())
# Check that the subpage is also gone
self.assertFalse(
StandardChild.objects.filter(id=self.grandchild_page.id).exists()
)
self.assertFalse(Page.objects.filter(id=self.grandchild_page.id).exists())
# Check that the signals were fired for both pages
self.assertIn(
(StandardIndex, self.child_index.id), unpublish_signals_received
)
self.assertIn(
(StandardChild, self.grandchild_page.id), unpublish_signals_received
)
self.assertIn(
(StandardIndex, self.child_index.id), pre_delete_signals_received
)
self.assertIn(
(StandardChild, self.grandchild_page.id), pre_delete_signals_received
)
self.assertIn(
(StandardIndex, self.child_index.id), post_delete_signals_received
)
self.assertIn(
(StandardChild, self.grandchild_page.id), post_delete_signals_received
)
finally:
page_unpublished.disconnect(page_unpublished_handler)
pre_delete.disconnect(pre_delete_handler)
post_delete.disconnect(post_delete_handler)
def test_before_delete_page_hook(self):
def hook_func(request, page):
self.assertIsInstance(request, HttpRequest)
self.assertEqual(page.id, self.child_page.id)
return HttpResponse("Overridden!")
with self.register_hook("before_delete_page", hook_func):
response = self.client.get(
reverse("wagtailadmin_pages:delete", args=(self.child_page.id,))
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
def test_before_delete_page_hook_post(self):
def hook_func(request, page):
self.assertIsInstance(request, HttpRequest)
self.assertEqual(page.id, self.child_page.id)
return HttpResponse("Overridden!")
with self.register_hook("before_delete_page", hook_func):
response = self.client.post(
reverse("wagtailadmin_pages:delete", args=(self.child_page.id,))
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# page should not be deleted
self.assertTrue(Page.objects.filter(id=self.child_page.id).exists())
def test_after_delete_page_hook(self):
def hook_func(request, page):
self.assertIsInstance(request, HttpRequest)
self.assertEqual(page.id, self.child_page.id)
return HttpResponse("Overridden!")
with self.register_hook("after_delete_page", hook_func):
response = self.client.post(
reverse("wagtailadmin_pages:delete", args=(self.child_page.id,))
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# page should be deleted
self.assertFalse(Page.objects.filter(id=self.child_page.id).exists())

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,264 @@
from unittest import mock
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
from django.contrib.messages import constants as message_constants
from django.http import HttpRequest, HttpResponse
from django.test import TestCase
from django.urls import reverse
from wagtail.models import Page
from wagtail.signals import post_page_move, pre_page_move
from wagtail.test.testapp.models import BusinessSubIndex, SimplePage
from wagtail.test.utils import WagtailTestUtils
class TestPageMove(WagtailTestUtils, TestCase):
fixtures = ["test.json"]
@classmethod
def setUpTestData(cls):
# Find root page
cls.root_page = Page.objects.get(id=2)
# Create three sections
cls.section_a = SimplePage(title="Section A", slug="section-a", content="hello")
cls.root_page.add_child(instance=cls.section_a)
cls.section_b = SimplePage(title="Section B", slug="section-b", content="hello")
cls.root_page.add_child(instance=cls.section_b)
cls.section_c = SimplePage(title="Section C", slug="section-c", content="hello")
cls.root_page.add_child(instance=cls.section_c)
# Add test page A into section A
cls.test_page_a = SimplePage(
title="Hello world!", slug="hello-world", content="hello"
)
cls.section_a.add_child(instance=cls.test_page_a)
# Add test page B into section C
cls.test_page_b = SimplePage(
title="Hello world!", slug="hello-world", content="hello"
)
cls.section_c.add_child(instance=cls.test_page_b)
# Add unpublished page to the root with a child page
cls.unpublished_page = SimplePage(
title="Unpublished", slug="unpublished", content="hello"
)
sub_page = SimplePage(title="Sub Page", slug="sub-page", content="child")
cls.root_page.add_child(instance=cls.unpublished_page)
cls.unpublished_page.add_child(instance=sub_page)
# unpublish pages last (used to validate the edit only permission)
cls.unpublished_page.unpublish()
sub_page.unpublish()
def setUp(self):
# Login
self.user = self.login()
def test_page_move(self):
response = self.client.get(
reverse("wagtailadmin_pages:move", args=(self.test_page_a.id,))
)
self.assertEqual(response.status_code, 200)
self.assertContains(response, self.section_a.title)
def test_page_move_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()
# Get move page
response = self.client.get(
reverse("wagtailadmin_pages:move", args=(self.test_page_a.id,))
)
# Check that the user received a 302 redirected response
self.assertEqual(response.status_code, 302)
def test_user_without_bulk_delete_permission_can_move(self):
# to verify that a user without bulk delete permission is able to move a page with a child page
self.client.logout()
user = get_user_model().objects.get(email="siteeditor@example.com")
self.login(user)
# ensure the bulk_delete is not applicable to this user
can_bulk_delete = self.test_page_b.permissions_for_user(user).can_delete()
self.assertFalse(can_bulk_delete)
response = self.client.get(
reverse("wagtailadmin_pages:move", args=(self.unpublished_page.id,))
)
self.assertEqual(response.status_code, 200)
def test_page_move_confirm(self):
response = self.client.get(
reverse(
"wagtailadmin_pages:move_confirm",
args=(self.test_page_a.id, self.section_b.id),
)
)
self.assertEqual(response.status_code, 200)
response = self.client.get(
reverse(
"wagtailadmin_pages:move_confirm",
args=(self.test_page_b.id, self.section_a.id),
)
)
# Duplicate slugs triggers a redirect with an error message.
self.assertEqual(response.status_code, 302)
response = self.client.get(reverse("wagtailadmin_home"))
messages = list(response.context["messages"])
self.assertEqual(len(messages), 1)
self.assertEqual(messages[0].level, message_constants.ERROR)
# Slug should be in error message.
self.assertIn(self.test_page_b.slug, messages[0].message)
def test_move_triggers_signals(self):
# Connect a mock signal handler to pre_page_move and post_page_move signals
pre_moved_handler = mock.MagicMock()
post_moved_handler = mock.MagicMock()
pre_page_move.connect(pre_moved_handler)
post_page_move.connect(post_moved_handler)
# Post to view to move page
try:
self.client.post(
reverse(
"wagtailadmin_pages:move_confirm",
args=(self.test_page_a.id, self.section_b.id),
)
)
finally:
# Disconnect mock handler to prevent cross-test pollution
pre_page_move.disconnect(pre_moved_handler)
post_page_move.disconnect(post_moved_handler)
# parent_page_before returns the non-specific page type, and that's OK
nonspecific_section_a = Page.objects.get(pk=self.section_a.pk)
# Check that the pre_page_move signal was fired
self.assertEqual(pre_moved_handler.call_count, 1)
pre_moved_handler.assert_called_with(
signal=mock.ANY,
sender=self.test_page_a.specific_class,
instance=self.test_page_a,
parent_page_before=nonspecific_section_a,
parent_page_after=self.section_b,
url_path_before="/home/section-a/hello-world/",
url_path_after="/home/section-b/hello-world/",
)
# Check that the post_page_move signal was fired
self.assertEqual(post_moved_handler.call_count, 1)
post_moved_handler.assert_called_with(
signal=mock.ANY,
sender=self.test_page_a.specific_class,
# during the move operation, we reloaded the page as a non-specific instance
instance=Page.objects.get(pk=self.test_page_a.pk),
parent_page_before=nonspecific_section_a,
parent_page_after=self.section_b,
url_path_before="/home/section-a/hello-world/",
url_path_after="/home/section-b/hello-world/",
)
def test_before_move_page_hook(self):
def hook_func(request, page, destination):
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(page.specific, SimplePage)
self.assertIsInstance(destination.specific, SimplePage)
return HttpResponse("Overridden!")
with self.register_hook("before_move_page", hook_func):
response = self.client.get(
reverse(
"wagtailadmin_pages:move_confirm",
args=(self.test_page_a.id, self.section_b.id),
)
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
def test_before_move_page_hook_post(self):
def hook_func(request, page, destination):
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(page.specific, SimplePage)
self.assertIsInstance(destination.specific, SimplePage)
return HttpResponse("Overridden!")
with self.register_hook("before_move_page", hook_func):
response = self.client.post(
reverse(
"wagtailadmin_pages:move_confirm",
args=(self.test_page_a.id, self.section_b.id),
)
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# page should not be moved
self.assertEqual(
Page.objects.get(id=self.test_page_a.id).get_parent().id, self.section_a.id
)
def test_after_move_page_hook(self):
def hook_func(request, page):
self.assertIsInstance(request, HttpRequest)
self.assertIsInstance(page.specific, SimplePage)
return HttpResponse("Overridden!")
with self.register_hook("after_move_page", hook_func):
response = self.client.post(
reverse(
"wagtailadmin_pages:move_confirm",
args=(self.test_page_a.id, self.section_b.id),
)
)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b"Overridden!")
# page should be moved
self.assertEqual(
Page.objects.get(id=self.test_page_a.id).get_parent().id, self.section_b.id
)
def test_page_move_after_parent_page_types_changes_to_different_parent_model(self):
# Test for issue #10348
# While BusinessSubIndex cannot be created under a SimplePage, we can
# still create it under a SimplePage invoking django-treebeard's add_child
# which works great for our purposes.
self.assertFalse(BusinessSubIndex.can_exist_under(self.section_a))
page = self.section_a.add_child(
instance=BusinessSubIndex(
title="Business Sub Index",
slug="business-sub-index",
live=True,
has_unpublished_changes=False,
)
)
response = self.client.get(reverse("wagtailadmin_pages:move", args=(page.id,)))
self.assertEqual(response.status_code, 200)
form = response.context["move_form"]
self.assertEqual(form.fields["new_parent_page"].initial.pk, self.section_a.pk)
self.assertNotContains(response, self.section_a.title)

View File

@@ -0,0 +1,345 @@
from django.contrib.auth.models import Group, Permission
from django.test import TestCase
from django.urls import reverse
from django.utils import timezone
from django.utils.html import escape
from wagtail.models import Page
from wagtail.test.testapp.models import SimplePage
from wagtail.test.utils import WagtailTestUtils
class TestLocking(WagtailTestUtils, TestCase):
def setUp(self):
# Find root page
self.root_page = Page.objects.get(id=2)
# Login
self.user = self.login()
# Create a page and submit it for moderation
self.child_page = SimplePage(
title="Hello world!",
slug="hello-world",
content="hello",
live=False,
)
self.root_page.add_child(instance=self.child_page)
def test_lock_post(self):
response = self.client.post(
reverse("wagtailadmin_pages:lock", args=(self.child_page.id,))
)
# Check response
self.assertRedirects(
response, reverse("wagtailadmin_explore", args=(self.root_page.id,))
)
# Check that the page is locked
page = Page.objects.get(id=self.child_page.id)
self.assertTrue(page.locked)
self.assertEqual(page.locked_by, self.user)
self.assertIsNotNone(page.locked_at)
def test_lock_get(self):
response = self.client.get(
reverse("wagtailadmin_pages:lock", args=(self.child_page.id,))
)
# Check response
self.assertEqual(response.status_code, 405)
# Check that the page is still unlocked
page = Page.objects.get(id=self.child_page.id)
self.assertFalse(page.locked)
self.assertIsNone(page.locked_by)
self.assertIsNone(page.locked_at)
def test_lock_post_already_locked(self):
# Lock the page
self.child_page.locked = True
self.child_page.locked_by = self.user
self.child_page.locked_at = timezone.now()
self.child_page.save()
response = self.client.post(
reverse("wagtailadmin_pages:lock", args=(self.child_page.id,))
)
# Check response
self.assertRedirects(
response, reverse("wagtailadmin_explore", args=(self.root_page.id,))
)
# Check that the page is still locked
page = Page.objects.get(id=self.child_page.id)
self.assertTrue(page.locked)
self.assertEqual(page.locked_by, self.user)
self.assertIsNotNone(page.locked_at)
def test_lock_post_with_good_redirect(self):
response = self.client.post(
reverse("wagtailadmin_pages:lock", args=(self.child_page.id,)),
{"next": reverse("wagtailadmin_pages:edit", args=(self.child_page.id,))},
)
# Check response
self.assertRedirects(
response, reverse("wagtailadmin_pages:edit", args=(self.child_page.id,))
)
# Check that the page is locked
page = Page.objects.get(id=self.child_page.id)
self.assertTrue(page.locked)
self.assertEqual(page.locked_by, self.user)
self.assertIsNotNone(page.locked_at)
def test_lock_post_with_bad_redirect(self):
response = self.client.post(
reverse("wagtailadmin_pages:lock", args=(self.child_page.id,)),
{"next": "http://www.google.co.uk"},
)
# Check response
self.assertRedirects(
response, reverse("wagtailadmin_explore", args=(self.root_page.id,))
)
# Check that the page is locked
page = Page.objects.get(id=self.child_page.id)
self.assertTrue(page.locked)
self.assertEqual(page.locked_by, self.user)
self.assertIsNotNone(page.locked_at)
def test_lock_post_bad_page(self):
response = self.client.post(reverse("wagtailadmin_pages:lock", args=(9999,)))
# Check response
self.assertEqual(response.status_code, 404)
# Check that the page is still unlocked
page = Page.objects.get(id=self.child_page.id)
self.assertFalse(page.locked)
self.assertIsNone(page.locked_by)
self.assertIsNone(page.locked_at)
def test_lock_post_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.post(
reverse("wagtailadmin_pages:lock", args=(self.child_page.id,))
)
# Check response
self.assertEqual(response.status_code, 302)
# Check that the page is still unlocked
page = Page.objects.get(id=self.child_page.id)
self.assertFalse(page.locked)
self.assertIsNone(page.locked_by)
self.assertIsNone(page.locked_at)
def test_locked_pages_dashboard_panel(self):
self.child_page.locked = True
self.child_page.locked_by = self.user
self.child_page.locked_at = timezone.now()
self.child_page.save()
response = self.client.get(reverse("wagtailadmin_home"))
self.assertContains(response, "Your locked pages")
# check that Unlock button is present
self.assertContains(response, "Unlock")
def test_unlock_post(self):
# Lock the page
self.child_page.locked = True
self.child_page.locked_by = self.user
self.child_page.locked_at = timezone.now()
self.child_page.save()
response = self.client.post(
reverse("wagtailadmin_pages:unlock", args=(self.child_page.id,))
)
# Check response
self.assertRedirects(
response, reverse("wagtailadmin_explore", args=(self.root_page.id,))
)
# Check that the page is unlocked
page = Page.objects.get(id=self.child_page.id)
self.assertFalse(page.locked)
self.assertIsNone(page.locked_by)
self.assertIsNone(page.locked_at)
def test_unlock_get(self):
# Lock the page
self.child_page.locked = True
self.child_page.locked_by = self.user
self.child_page.locked_at = timezone.now()
self.child_page.save()
response = self.client.get(
reverse("wagtailadmin_pages:unlock", args=(self.child_page.id,))
)
# Check response
self.assertEqual(response.status_code, 405)
# Check that the page is still locked
page = Page.objects.get(id=self.child_page.id)
self.assertTrue(page.locked)
self.assertEqual(page.locked_by, self.user)
self.assertIsNotNone(page.locked_at)
def test_unlock_post_already_unlocked(self):
response = self.client.post(
reverse("wagtailadmin_pages:unlock", args=(self.child_page.id,))
)
# Check response
self.assertRedirects(
response, reverse("wagtailadmin_explore", args=(self.root_page.id,))
)
# Check that the page is still unlocked
page = Page.objects.get(id=self.child_page.id)
self.assertFalse(page.locked)
self.assertIsNone(page.locked_by)
self.assertIsNone(page.locked_at)
def test_unlock_post_with_good_redirect(self):
# Lock the page
self.child_page.locked = True
self.child_page.locked_by = self.user
self.child_page.locked_at = timezone.now()
self.child_page.save()
response = self.client.post(
reverse("wagtailadmin_pages:unlock", args=(self.child_page.id,)),
{"next": reverse("wagtailadmin_pages:edit", args=(self.child_page.id,))},
follow=True,
)
# Check response
self.assertRedirects(
response, reverse("wagtailadmin_pages:edit", args=(self.child_page.id,))
)
# Should show unlocked message
self.assertContains(
response, escape("Page 'Hello world! (simple page)' is now unlocked.")
)
# Message shouldn't be wrapped in a tuple
self.assertNotContains(
response,
escape(("Page 'Hello world! (simple page)' is now unlocked.",)),
)
# Check that the page is unlocked
page = Page.objects.get(id=self.child_page.id)
self.assertFalse(page.locked)
self.assertIsNone(page.locked_by)
self.assertIsNone(page.locked_at)
def test_unlock_post_with_bad_redirect(self):
# Lock the page
self.child_page.locked = True
self.child_page.locked_by = self.user
self.child_page.locked_at = timezone.now()
self.child_page.save()
response = self.client.post(
reverse("wagtailadmin_pages:unlock", args=(self.child_page.id,)),
{"next": "http://www.google.co.uk"},
)
# Check response
self.assertRedirects(
response, reverse("wagtailadmin_explore", args=(self.root_page.id,))
)
# Check that the page is unlocked
page = Page.objects.get(id=self.child_page.id)
self.assertFalse(page.locked)
self.assertIsNone(page.locked_by)
self.assertIsNone(page.locked_at)
def test_unlock_post_bad_page(self):
# Lock the page
self.child_page.locked = True
self.child_page.locked_by = self.user
self.child_page.locked_at = timezone.now()
self.child_page.save()
response = self.client.post(reverse("wagtailadmin_pages:unlock", args=(9999,)))
# Check response
self.assertEqual(response.status_code, 404)
# Check that the page is still locked
page = Page.objects.get(id=self.child_page.id)
self.assertTrue(page.locked)
self.assertEqual(page.locked_by, self.user)
self.assertIsNotNone(page.locked_at)
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 page
self.child_page.locked = True
self.child_page.locked_at = timezone.now()
self.child_page.save()
response = self.client.post(
reverse("wagtailadmin_pages:unlock", args=(self.child_page.id,))
)
# Check response
self.assertEqual(response.status_code, 302)
# Check that the page is still locked
page = Page.objects.get(id=self.child_page.id)
self.assertTrue(page.locked)
self.assertIsNotNone(page.locked_at)
def test_unlock_post_own_page_with_bad_permissions(self):
# Unlike the previous test, the user can unlock pages that they have locked
# Remove privileges from user
self.user.is_superuser = False
self.user.groups.add(Group.objects.get(name="Editors"))
self.user.save()
# Lock the page
self.child_page.locked = True
self.child_page.locked_by = self.user
self.child_page.locked_at = timezone.now()
self.child_page.save()
response = self.client.post(
reverse("wagtailadmin_pages:unlock", args=(self.child_page.id,)),
{"next": reverse("wagtailadmin_pages:edit", args=(self.child_page.id,))},
)
# Check response
self.assertRedirects(
response, reverse("wagtailadmin_pages:edit", args=(self.child_page.id,))
)
# Check that the page is still locked
page = Page.objects.get(id=self.child_page.id)
self.assertFalse(page.locked)
self.assertIsNone(page.locked_by)
self.assertIsNone(page.locked_at)

View File

@@ -0,0 +1,329 @@
from io import StringIO
from django.contrib.auth.models import Permission
from django.core import management
from django.test import TransactionTestCase
from django.urls import reverse
from django.utils.http import urlencode
from wagtail.models import Page
from wagtail.test.testapp.models import EventIndex, SimplePage, SingleEventPage
from wagtail.test.utils import WagtailTestUtils
from wagtail.test.utils.timestamps import local_datetime
class TestPageSearch(WagtailTestUtils, TransactionTestCase):
fixtures = ["test_empty.json"]
def setUp(self):
super().setUp()
management.call_command(
"update_index",
backend_name="default",
stdout=StringIO(),
chunk_size=50,
)
self.user = self.login()
def get(self, params=None, url_name="wagtailadmin_pages:search", **extra):
return self.client.get(reverse(url_name), params or {}, **extra)
def test_view(self):
response = self.get()
self.assertTemplateUsed(response, "wagtailadmin/pages/search.html")
self.assertEqual(response.status_code, 200)
def test_search(self):
# Find root page
root_page = Page.objects.get(id=2)
# Create a page
new_page = root_page.add_child(
instance=SimplePage(
title="Hello from Cauldron Lake",
slug="bright-falls",
content="It's not a lake, it's an ocean",
live=True,
has_unpublished_changes=False,
)
)
response = self.get({"q": "Hello"})
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "wagtailadmin/pages/search.html")
self.assertEqual(response.context["query_string"], "Hello")
next_url = urlencode({"next": reverse("wagtailadmin_pages:search")})
expected_new_page_copy_url = (
reverse("wagtailadmin_pages:copy", args=(new_page.pk,)) + f"?{next_url}"
)
self.assertContains(response, f'href="{expected_new_page_copy_url}"')
def test_search_searchable_fields(self):
# Find root page
root_page = Page.objects.get(id=2)
# Create a page
root_page.add_child(
instance=SimplePage(
title="Greetings!",
slug="hello",
content="good morning",
live=True,
has_unpublished_changes=False,
)
)
# Confirm the slug is not being searched
response = self.get({"q": "hello"})
self.assertNotContains(response, "There is one matching page")
# Confirm the title is being searched
response = self.get({"q": "greetings"})
self.assertContains(response, "There is one matching page")
def test_ajax(self):
# Find root page
root_page = Page.objects.get(id=2)
# Create a page
new_page = root_page.add_child(
instance=SimplePage(
title="Hello from Cauldron Lake",
slug="bright-falls",
content="It's not a lake, it's an ocean",
live=True,
has_unpublished_changes=False,
)
)
response = self.get(
{"q": "Hello"}, url_name="wagtailadmin_pages:search_results"
)
self.assertEqual(response.status_code, 200)
self.assertTemplateNotUsed(response, "wagtailadmin/pages/search.html")
self.assertTemplateUsed(response, "wagtailadmin/pages/search_results.html")
self.assertEqual(response.context["query_string"], "Hello")
next_url = urlencode({"next": reverse("wagtailadmin_pages:search")})
expected_new_page_copy_url = (
reverse("wagtailadmin_pages:copy", args=(new_page.pk,)) + f"?{next_url}"
)
self.assertContains(response, f'href="{expected_new_page_copy_url}"')
def test_pagination(self):
# page numbers in range should be accepted
response = self.get({"q": "Hello", "p": 1})
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "wagtailadmin/pages/search.html")
# page numbers out of range should return 404
response = self.get({"q": "Hello", "p": 9999})
self.assertEqual(response.status_code, 404)
def test_root_can_appear_in_search_results(self):
response = self.get({"q": "root"})
self.assertEqual(response.status_code, 200)
# 'pages' list in the response should contain root
results = response.context["pages"]
self.assertTrue(any(r.slug == "root" for r in results))
def test_search_uses_admin_display_title_from_specific_class(self):
# SingleEventPage has a custom get_admin_display_title method; explorer should
# show the custom title rather than the basic database one
root_page = Page.objects.get(id=2)
new_event = SingleEventPage(
title="Lunar event",
location="the moon",
audience="public",
cost="free",
date_from="2001-01-01",
latest_revision_created_at=local_datetime(2016, 1, 1),
)
root_page.add_child(instance=new_event)
response = self.get({"q": "lunar"})
self.assertContains(response, "Lunar event (single event)")
def test_search_no_perms(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()
self.assertRedirects(self.get(), "/admin/")
def test_search_order_by_title(self):
root_page = Page.objects.get(id=2)
new_event = SingleEventPage(
title="Lunar event",
location="the moon",
audience="public",
cost="free",
date_from="2001-01-01",
latest_revision_created_at=local_datetime(2016, 1, 1),
)
root_page.add_child(instance=new_event)
new_event_2 = SingleEventPage(
title="A Lunar event",
location="the moon",
audience="public",
cost="free",
date_from="2001-01-01",
latest_revision_created_at=local_datetime(2016, 1, 1),
)
root_page.add_child(instance=new_event_2)
response = self.get({"q": "Lunar", "ordering": "title"})
page_ids = [page.id for page in response.context["pages"]]
self.assertEqual(page_ids, [new_event_2.id, new_event.id])
response = self.get({"q": "Lunar", "ordering": "-title"})
page_ids = [page.id for page in response.context["pages"]]
self.assertEqual(page_ids, [new_event.id, new_event_2.id])
def test_search_order_by_updated(self):
root_page = Page.objects.get(id=2)
new_event = SingleEventPage(
title="Lunar event",
location="the moon",
audience="public",
cost="free",
date_from="2001-01-01",
latest_revision_created_at=local_datetime(2016, 1, 1),
)
root_page.add_child(instance=new_event)
new_event_2 = SingleEventPage(
title="Lunar event 2",
location="the moon",
audience="public",
cost="free",
date_from="2001-01-01",
latest_revision_created_at=local_datetime(2015, 1, 1),
)
root_page.add_child(instance=new_event_2)
response = self.get({"q": "Lunar", "ordering": "latest_revision_created_at"})
page_ids = [page.id for page in response.context["pages"]]
self.assertEqual(page_ids, [new_event_2.id, new_event.id])
response = self.get({"q": "Lunar", "ordering": "-latest_revision_created_at"})
page_ids = [page.id for page in response.context["pages"]]
self.assertEqual(page_ids, [new_event.id, new_event_2.id])
def test_search_order_by_status(self):
root_page = Page.objects.get(id=2)
live_event = SingleEventPage(
title="Lunar event",
location="the moon",
audience="public",
cost="free",
date_from="2001-01-01",
latest_revision_created_at=local_datetime(2016, 1, 1),
live=True,
)
root_page.add_child(instance=live_event)
draft_event = SingleEventPage(
title="Lunar event",
location="the moon",
audience="public",
cost="free",
date_from="2001-01-01",
latest_revision_created_at=local_datetime(2016, 1, 1),
live=False,
)
root_page.add_child(instance=draft_event)
response = self.get({"q": "Lunar", "ordering": "live"})
page_ids = [page.id for page in response.context["pages"]]
self.assertEqual(page_ids, [draft_event.id, live_event.id])
response = self.get({"q": "Lunar", "ordering": "-live"})
page_ids = [page.id for page in response.context["pages"]]
self.assertEqual(page_ids, [live_event.id, draft_event.id])
def test_search_filter_content_type(self):
# Correct content_type
response = self.get({"content_type": "demosite.standardpage"})
self.assertEqual(response.status_code, 200)
# Incorrect content_type
response = self.get({"content_type": "demosite.standardpage.error"})
self.assertEqual(response.status_code, 404)
def test_empty_search_renders_content_type_facets(self):
root_page = Page.objects.get(id=2)
event_index = EventIndex(
title="ALL THE EVENTS",
intro="It's just a nod to the canon",
)
root_page.add_child(instance=event_index)
params = [{"q": ""}, {}]
url = reverse("wagtailadmin_pages:search")
for param in params:
with self.subTest(param=param):
response = self.get(param)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "wagtailadmin/pages/search.html")
self.assertEqual(response.context["query_string"], "")
self.assertContains(response, "Page types")
self.assertContains(response, "All (3)")
# The test fixture contains the root page and the welcome page
# with the base page type
self.assertContains(response, "Page (2)")
self.assertContains(response, "ALL THE EVENTS")
self.assertContains(response, "Event index (1)")
self.assertContains(
response,
f"{url}?q=&amp;content_type=tests.eventindex",
)
def test_empty_search_with_content_type_filter(self):
root_page = Page.objects.get(id=2)
event_index = EventIndex(
title="ALL THE EVENTS",
intro="It's just a nod to the canon",
)
new_event = SingleEventPage(
title="Lunar event",
location="the moon",
audience="public",
cost="free",
date_from="2001-01-01",
latest_revision_created_at=local_datetime(2016, 1, 1),
)
root_page.add_child(instance=event_index)
root_page.add_child(instance=new_event)
params = [
{"q": "", "content_type": "tests.singleeventpage"},
{"content_type": "tests.singleeventpage"},
]
url = reverse("wagtailadmin_pages:search")
for param in params:
with self.subTest(param=param):
response = self.get(param)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "wagtailadmin/pages/search.html")
self.assertEqual(response.context["query_string"], "")
self.assertContains(response, "Page types")
self.assertContains(response, "All (4)")
# The test fixture contains the root page and the welcome page
# with the base page type
self.assertContains(response, "Page (2)")
self.assertContains(response, "Single event page (1)")
self.assertContains(response, "Lunar event")
self.assertNotContains(response, "ALL THE EVENTS")
self.assertContains(response, "Event index (1)")
self.assertContains(
response,
f"{url}?q=&amp;content_type=tests.eventindex",
)

View File

@@ -0,0 +1,122 @@
from django.test import TestCase
from django.urls import reverse
from wagtail.models import Page
from wagtail.test.testapp.models import (
FormPageWithRedirect,
PageChooserModel,
SimplePage,
)
from wagtail.test.utils import WagtailTestUtils
from wagtail.test.utils.template_tests import AdminTemplateTestUtils
class TestPageUsage(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
# We don't show the "Home" breadcrumb item in page views
base_breadcrumb_items = []
def setUp(self):
self.user = self.login()
self.root_page = Page.objects.get(id=2)
page = SimplePage(
title="Hello world!",
slug="hello-world",
content="hello",
)
self.root_page.add_child(instance=page)
page.save_revision().publish()
self.page = SimplePage.objects.get(id=page.id)
def test_simple(self):
usage_url = reverse("wagtailadmin_pages:usage", args=(self.page.id,))
response = self.client.get(usage_url)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "wagtailadmin/generic/listing.html")
self.assertContains(response, "Usage")
self.assertContains(response, "Hello world!")
items = [
{
"url": reverse("wagtailadmin_explore_root"),
"label": "Root",
},
{
"url": reverse("wagtailadmin_explore", args=(self.root_page.id,)),
"label": "Welcome to your new Wagtail site!",
},
{
"url": reverse("wagtailadmin_explore", args=(self.page.id,)),
"label": "Hello world! (simple page)",
},
{
"url": "",
"label": "Usage",
"sublabel": "Hello world! (simple page)",
},
]
self.assertBreadcrumbsItemsRendered(items, response.content)
# There should be exactly one edit link, rendered as a header button
edit_url = reverse("wagtailadmin_pages:edit", args=(self.page.id,))
soup = self.get_soup(response.content)
edit_links = soup.select(f"a[href='{edit_url}']")
self.assertEqual(len(edit_links), 1)
edit_link = edit_links[0]
classes = edit_link.attrs.get("class")
self.assertIn("w-header-button", classes)
self.assertIn("button", classes)
def test_has_private_usage(self):
PageChooserModel.objects.create(page=self.page)
usage_url = reverse("wagtailadmin_pages:usage", args=(self.page.id,))
response = self.client.get(usage_url)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "wagtailadmin/generic/listing.html")
self.assertContains(response, "Usage")
self.assertContains(response, "Hello world!")
self.assertContains(response, "(Private page chooser model)")
self.assertContains(response, "<td>Page chooser model</td>", html=True)
def test_has_editable_usage(self):
form_page = FormPageWithRedirect(
title="Contact us",
slug="contact-us",
to_address="to@email.com",
from_address="from@email.com",
subject="The subject",
thank_you_redirect_page=self.page,
)
form_page = self.root_page.add_child(instance=form_page)
usage_url = reverse("wagtailadmin_pages:usage", args=(self.page.id,))
response = self.client.get(usage_url)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "wagtailadmin/generic/listing.html")
self.assertContains(response, "Usage")
self.assertContains(response, "Hello world!")
self.assertContains(response, "Contact us")
self.assertContains(
response, reverse("wagtailadmin_pages:edit", args=(form_page.id,))
)
self.assertContains(response, "Thank you redirect page")
self.assertContains(response, "<td>Form page with redirect</td>", html=True)
def test_pagination(self):
for _ in range(50):
PageChooserModel.objects.create(page=self.page)
usage_url = reverse("wagtailadmin_pages:usage", args=(self.page.id,))
response = self.client.get(f"{usage_url}?p=2")
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "wagtailadmin/generic/listing.html")
self.assertContains(response, "Page 2 of 3.")
self.assertContains(response, f"{usage_url}?p=1")
self.assertContains(response, f"{usage_url}?p=3")

Some files were not shown because too many files have changed in this diff Show More