405 lines
15 KiB
Python
405 lines
15 KiB
Python
from django.contrib.auth import get_user_model
|
|
from django.contrib.auth.models import Group, Permission
|
|
from django.contrib.contenttypes.models import ContentType
|
|
from django.test import TestCase
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
from freezegun import freeze_time
|
|
|
|
from wagtail.admin.views.home import (
|
|
LockedPagesPanel,
|
|
RecentEditsPanel,
|
|
UserObjectsInWorkflowModerationPanel,
|
|
WorkflowObjectsToModeratePanel,
|
|
)
|
|
from wagtail.coreutils import get_dummy_request
|
|
from wagtail.models import GroupPagePermission, Page, Workflow, WorkflowContentType
|
|
from wagtail.test.testapp.models import FullFeaturedSnippet, SimplePage
|
|
from wagtail.test.utils import WagtailTestUtils
|
|
|
|
|
|
class TestRecentEditsPanel(WagtailTestUtils, TestCase):
|
|
def setUp(self):
|
|
# Find root page
|
|
self.root_page = Page.objects.get(id=2)
|
|
|
|
# Add child page
|
|
child_page = SimplePage(
|
|
title="Hello world!",
|
|
slug="hello-world",
|
|
content="Some content here",
|
|
)
|
|
self.root_page.add_child(instance=child_page)
|
|
self.revision = child_page.save_revision()
|
|
self.revision.publish()
|
|
self.child_page = SimplePage.objects.get(id=child_page.id)
|
|
|
|
self.user_alice = self.create_superuser(username="alice", password="password")
|
|
self.create_superuser(username="bob", password="password")
|
|
|
|
def change_something(self, title):
|
|
post_data = {"title": title, "content": "Some content", "slug": "hello-world"}
|
|
response = self.client.post(
|
|
reverse("wagtailadmin_pages:edit", args=(self.child_page.id,)), post_data
|
|
)
|
|
|
|
# Should be redirected to edit page
|
|
self.assertRedirects(
|
|
response, reverse("wagtailadmin_pages:edit", args=(self.child_page.id,))
|
|
)
|
|
|
|
# The page should have "has_unpublished_changes" flag set
|
|
child_page_new = SimplePage.objects.get(id=self.child_page.id)
|
|
self.assertTrue(child_page_new.has_unpublished_changes)
|
|
|
|
def go_to_dashboard_response(self):
|
|
response = self.client.get(reverse("wagtailadmin_home"))
|
|
self.assertEqual(response.status_code, 200)
|
|
return response
|
|
|
|
def test_your_recent_edits(self):
|
|
# Login as Bob
|
|
self.login(username="bob", password="password")
|
|
|
|
# Bob hasn't edited anything yet
|
|
response = self.client.get(reverse("wagtailadmin_home"))
|
|
self.assertNotIn("Your most recent edits", response.content.decode("utf-8"))
|
|
|
|
# Login as Alice
|
|
self.client.logout()
|
|
self.login(username="alice", password="password")
|
|
|
|
# Alice changes something
|
|
self.change_something("Alice's edit")
|
|
|
|
# Edit should show up on dashboard
|
|
response = self.go_to_dashboard_response()
|
|
self.assertIn("Your most recent edits", response.content.decode("utf-8"))
|
|
|
|
# Bob changes something
|
|
self.login(username="bob", password="password")
|
|
self.change_something("Bob's edit")
|
|
|
|
# Edit shows up on Bobs dashboard
|
|
response = self.go_to_dashboard_response()
|
|
self.assertIn("Your most recent edits", response.content.decode("utf-8"))
|
|
|
|
# Login as Alice again
|
|
self.client.logout()
|
|
self.login(username="alice", password="password")
|
|
|
|
# Alice's dashboard should still list that first edit
|
|
response = self.go_to_dashboard_response()
|
|
self.assertIn("Your most recent edits", response.content.decode("utf-8"))
|
|
|
|
def test_missing_page_record(self):
|
|
# Ensure that the panel still renders when one of the page IDs returned from querying
|
|
# PageLogEntry has no corresponding Page object. This can happen if a page is deleted,
|
|
# because PageLogEntry records are kept on deletion.
|
|
|
|
self.login(username="alice", password="password")
|
|
self.change_something("Alice's edit")
|
|
self.child_page.delete()
|
|
response = self.client.get(reverse("wagtailadmin_home"))
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
def test_panel(self):
|
|
"""Test if the panel actually returns expected pages"""
|
|
self.login(username="bob", password="password")
|
|
# change a page
|
|
|
|
edit_timestamp = timezone.now()
|
|
with freeze_time(edit_timestamp):
|
|
self.change_something("Bob's edit")
|
|
|
|
# set a user to 'mock' a request
|
|
self.client.user = get_user_model().objects.get(email="bob@example.com")
|
|
# get the panel to get the last edits
|
|
panel = RecentEditsPanel()
|
|
ctx = panel.get_context_data({"request": self.client})
|
|
|
|
page = Page.objects.get(pk=self.child_page.id).specific
|
|
|
|
# check the timestamp matches the edit
|
|
self.assertEqual(ctx["last_edits"][0][0], edit_timestamp)
|
|
# check if the page in this list is the specific page
|
|
self.assertEqual(ctx["last_edits"][0][1], page)
|
|
|
|
def test_copying_does_not_count_as_an_edit(self):
|
|
self.login(username="bob", password="password")
|
|
# change a page
|
|
self.change_something("Bob was ere")
|
|
|
|
# copy the page
|
|
post_data = {
|
|
"new_title": "Goodbye world!",
|
|
"new_slug": "goodbye-world",
|
|
"new_parent_page": str(self.root_page.id),
|
|
"copy_subpages": False,
|
|
"alias": False,
|
|
}
|
|
self.client.post(
|
|
reverse("wagtailadmin_pages:copy", args=(self.child_page.id,)), post_data
|
|
)
|
|
# check that page has been copied
|
|
self.assertTrue(Page.objects.get(title="Goodbye world!"))
|
|
|
|
response = self.client.get(reverse("wagtailadmin_home"))
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertContains(response, "Your most recent edits")
|
|
self.assertContains(response, "Bob was ere")
|
|
self.assertNotContains(response, "Goodbye world!")
|
|
|
|
|
|
class TestRecentEditsQueryCount(WagtailTestUtils, TestCase):
|
|
fixtures = ["test.json"]
|
|
|
|
def setUp(self):
|
|
self.bob = self.create_superuser(username="bob", password="password")
|
|
self.dummy_request = get_dummy_request()
|
|
self.dummy_request.user = self.bob
|
|
workflow = Workflow.objects.first()
|
|
workflow_pages = {5, 6}
|
|
locked_pages = {6, 9}
|
|
scheduled_pages = {9, 12}
|
|
# make a bunch of page edits (all to EventPages, so that calls to specific() don't add
|
|
# an unpredictable number of queries)
|
|
pages_to_edit = list(
|
|
Page.objects.filter(id__in=[4, 5, 6, 9, 12, 13]).order_by("pk").specific()
|
|
)
|
|
for page in pages_to_edit:
|
|
revision = page.save_revision(user=self.bob, log_action=True)
|
|
if page.pk in workflow_pages:
|
|
workflow.start(page, self.bob)
|
|
if page.pk in locked_pages:
|
|
page.locked = True
|
|
page.locked_by = self.bob
|
|
page.locked_at = timezone.now()
|
|
page.save()
|
|
if page.pk in scheduled_pages:
|
|
revision.approved_go_live_at = timezone.now()
|
|
revision.save()
|
|
|
|
def test_panel_query_count(self):
|
|
panel = RecentEditsPanel()
|
|
parent_context = {"request": self.dummy_request}
|
|
# Warm up the cache
|
|
html = panel.render_html(parent_context)
|
|
|
|
with self.assertNumQueries(5):
|
|
# Rendering RecentEditsPanel should not generate N+1 queries -
|
|
# i.e. any number less than 6 would be reasonable here
|
|
html = panel.render_html(parent_context)
|
|
# check that the panel is still actually returning results
|
|
self.assertIn("Ameristralia Day", html)
|
|
soup = self.get_soup(html)
|
|
self.assertEqual(len(soup.select('svg use[href="#icon-lock"]')), 2)
|
|
expected_statuses = [
|
|
"live + draft",
|
|
"live + scheduled",
|
|
"live + scheduled",
|
|
"in moderation",
|
|
"in moderation",
|
|
]
|
|
statuses = [
|
|
"".join(e.find_all(string=True, recursive=False)).strip()
|
|
for e in soup.select(".w-status")
|
|
]
|
|
self.assertEqual(statuses, expected_statuses)
|
|
|
|
|
|
class TestLockedPagesQueryCount(WagtailTestUtils, TestCase):
|
|
fixtures = ["test.json"]
|
|
|
|
def setUp(self):
|
|
self.bob = self.create_superuser(username="bob", password="password")
|
|
self.dummy_request = get_dummy_request()
|
|
self.dummy_request.user = self.bob
|
|
|
|
pages = Page.objects.filter(pk__in=[9, 12, 13]).order_by("pk")
|
|
for i, page in enumerate(pages):
|
|
page.locked = True
|
|
page.locked_by = self.bob
|
|
page.locked_at = timezone.now() + timezone.timedelta(hours=i)
|
|
page.save()
|
|
|
|
def test_panel_query_count(self):
|
|
panel = LockedPagesPanel()
|
|
parent_context = {"request": self.dummy_request, "csrf_token": "dummy"}
|
|
# Warm up the cache
|
|
html = panel.render_html(parent_context)
|
|
|
|
with self.assertNumQueries(1):
|
|
html = panel.render_html(parent_context)
|
|
soup = self.get_soup(html)
|
|
# Should be sorted descending by locked_at
|
|
expected_titles = [
|
|
"Saint Patrick (single event)",
|
|
"Steal underpants",
|
|
"Ameristralia Day",
|
|
]
|
|
titles = [e.get_text(strip=True) for e in soup.select(".title-wrapper a")]
|
|
self.assertEqual(titles, expected_titles)
|
|
|
|
|
|
class UserObjectsInWorkflowModerationQueryCount(WagtailTestUtils, TestCase):
|
|
fixtures = ["test.json"]
|
|
|
|
def setUp(self):
|
|
self.superuser = self.create_superuser(username="admin", password="password")
|
|
self.bob = self.create_user(username="bob", password="password")
|
|
self.someone_else = self.create_user(
|
|
username="someoneelse", password="password"
|
|
)
|
|
editors = Group.objects.get(name="Editors")
|
|
editors.user_set.add(self.bob, self.someone_else)
|
|
|
|
workflow = Workflow.objects.first()
|
|
WorkflowContentType.objects.create(
|
|
workflow=workflow,
|
|
content_type=ContentType.objects.get_for_model(FullFeaturedSnippet),
|
|
)
|
|
GroupPagePermission.objects.create(
|
|
group=editors, page=Page.get_first_root_node(), permission_type="change"
|
|
)
|
|
editors.permissions.add(
|
|
Permission.objects.get(codename="change_fullfeaturedsnippet")
|
|
)
|
|
|
|
# Pages owned by bob, but workflow started by someone else
|
|
Page.objects.filter(id__in=[9, 12]).update(owner=self.bob)
|
|
for page in Page.objects.filter(id__in=[9, 12]).specific():
|
|
page.save_revision()
|
|
workflow.start(page, self.someone_else)
|
|
# Lock it to test the lock indicator
|
|
page.locked = True
|
|
page.locked_by = self.superuser
|
|
page.locked_at = timezone.now()
|
|
page.save()
|
|
|
|
# Page workflow started by bob
|
|
for page in Page.objects.filter(id__in=[4, 13]).specific():
|
|
page.save_revision()
|
|
workflow.start(page, self.bob)
|
|
|
|
# Snippet workflow started by bob
|
|
for i in range(1, 3):
|
|
obj = FullFeaturedSnippet.objects.create(text=f"Some obj {i}")
|
|
obj.save_revision()
|
|
workflow.start(obj, self.bob)
|
|
|
|
self.dummy_request = get_dummy_request()
|
|
self.dummy_request.user = self.bob
|
|
|
|
def test_panel_query_count(self):
|
|
panel = UserObjectsInWorkflowModerationPanel()
|
|
parent_context = {"request": self.dummy_request}
|
|
# Warm up the cache
|
|
html = panel.render_html(parent_context)
|
|
|
|
with self.assertNumQueries(4):
|
|
html = panel.render_html(parent_context)
|
|
|
|
soup = self.get_soup(html)
|
|
self.assertEqual(len(soup.select('svg use[href="#icon-lock"]')), 2)
|
|
expected_titles = [
|
|
"Some obj 2",
|
|
"Some obj 1",
|
|
"Saint Patrick (single event)",
|
|
"Christmas",
|
|
"Steal underpants",
|
|
"Ameristralia Day",
|
|
]
|
|
titles = [e.get_text(strip=True) for e in soup.select(".title-wrapper a")]
|
|
self.assertEqual(titles, expected_titles)
|
|
|
|
|
|
class WorkflowObjectsToModerateQueryCount(WagtailTestUtils, TestCase):
|
|
fixtures = ["test.json"]
|
|
|
|
def setUp(self):
|
|
self.superuser = self.create_superuser(username="admin", password="password")
|
|
self.bob = self.create_user(username="bob", password="password")
|
|
self.moderator = self.create_user(username="moderator", password="password")
|
|
|
|
editors = Group.objects.get(name="Editors")
|
|
moderators = Group.objects.get(name="Moderators")
|
|
|
|
editors.user_set.add(self.bob)
|
|
moderators.user_set.add(self.moderator)
|
|
|
|
root = Page.get_first_root_node()
|
|
GroupPagePermission.objects.create(
|
|
group=editors, page=root, permission_type="change"
|
|
)
|
|
GroupPagePermission.objects.create(
|
|
group=moderators, page=root, permission_type="change"
|
|
)
|
|
GroupPagePermission.objects.create(
|
|
group=moderators, page=root, permission_type="publish"
|
|
)
|
|
|
|
editors.permissions.add(
|
|
Permission.objects.get(codename="change_fullfeaturedsnippet")
|
|
)
|
|
moderators.permissions.add(
|
|
*Permission.objects.filter(
|
|
codename__in=[
|
|
"change_fullfeaturedsnippet",
|
|
"publish_fullfeaturedsnippet",
|
|
]
|
|
),
|
|
)
|
|
|
|
workflow = Workflow.objects.first()
|
|
WorkflowContentType.objects.create(
|
|
workflow=workflow,
|
|
content_type=ContentType.objects.get_for_model(FullFeaturedSnippet),
|
|
)
|
|
|
|
# Pages workflow started by bob and locked by moderator
|
|
for page in Page.objects.filter(id__in=[9, 12]).specific():
|
|
page.save_revision()
|
|
workflow.start(page, self.bob)
|
|
# Lock it to test the lock indicator
|
|
page.locked = True
|
|
page.locked_by = self.moderator
|
|
page.locked_at = timezone.now()
|
|
page.save()
|
|
|
|
# Page workflow started by bob
|
|
for page in Page.objects.filter(id__in=[4, 13]).specific():
|
|
page.save_revision()
|
|
workflow.start(page, self.bob)
|
|
|
|
# Snippet workflow started by bob
|
|
for i in range(1, 3):
|
|
obj = FullFeaturedSnippet.objects.create(text=f"Some obj {i}")
|
|
obj.save_revision()
|
|
workflow.start(obj, self.bob)
|
|
|
|
self.dummy_request = get_dummy_request()
|
|
self.dummy_request.user = self.moderator
|
|
|
|
def test_panel_query_count(self):
|
|
panel = WorkflowObjectsToModeratePanel()
|
|
parent_context = {"request": self.dummy_request, "csrf_token": "dummy"}
|
|
# Warm up the cache
|
|
html = panel.render_html(parent_context)
|
|
|
|
with self.assertNumQueries(13):
|
|
html = panel.render_html(parent_context)
|
|
|
|
soup = self.get_soup(html)
|
|
self.assertEqual(len(soup.select('svg use[href="#icon-lock"]')), 2)
|
|
expected_titles = [
|
|
"Some obj 2",
|
|
"Some obj 1",
|
|
"Saint Patrick (single event)",
|
|
"Christmas",
|
|
"Steal underpants",
|
|
"Ameristralia Day",
|
|
]
|
|
titles = [e.get_text(strip=True) for e in soup.select(".title-wrapper a")]
|
|
self.assertEqual(titles, expected_titles)
|