Files
guilan-ace-backend/apps/content/tests/integration/test_admin_workflow.py

213 lines
11 KiB
Python

import io
import pytest
from django.contrib.auth.models import Group
from django.core.files.uploadedfile import SimpleUploadedFile
from django.urls import reverse
from PIL import Image
from apps.content.models import EditorialPage, MediaAsset, PageRevision, SiteIdentity
from apps.users.models import User
pytestmark = [pytest.mark.django_db, pytest.mark.integration]
def staff(name, group):
user = User.objects.create_user(username=name, mobile=f"0912{User.objects.count():07d}", password="TestPass123!")
user.is_staff = True
user.save(update_fields=["is_staff"])
user.groups.add(Group.objects.get(name=group))
return user
def home_post():
data = {"title": "Home", "intro": "Welcome", "seo_title": "Home", "seo_description": "Association"}
for position, key in enumerate(("hero", "featured_events", "featured_posts", "participation")):
data[f"section_{key}-heading"] = key.replace("_", " ").title()
if key in {"featured_events", "featured_posts"}:
data[f"section_{key}-all_link_label"] = "See all"
data[f"section_{key}-body"] = "Introduction" if key in {"hero", "participation"} else ""
return data
def test_editor_can_save_and_preview_draft_without_publishing(client):
page = EditorialPage.objects.create(slug="home", page_kind="sections")
editor = staff("editor", "content_editor")
client.force_login(editor)
draft_url = reverse("admin:content_editorialpage_draft", args=[page.pk])
response = client.get(draft_url)
assert response.status_code == 200
assert b"section_hero-heading" in response.content
assert b"primary_action" not in response.content
response = client.post(draft_url, home_post(), follow=True)
assert response.status_code == 200
revision = PageRevision.objects.get(page=page)
assert revision.status == "draft"
assert page.published_revision_id is None
preview_url = reverse("admin:content_editorialpage_preview", args=[page.pk, revision.pk])
response = client.get(preview_url)
assert response.status_code == 200
assert b"Home" in response.content
assert "private" in response["Cache-Control"] and "no-store" in response["Cache-Control"]
publish_url = reverse("admin:content_editorialpage_publish", args=[page.pk, revision.pk])
assert client.post(publish_url, {"expected_published_id": ""}).status_code == 403
def test_publisher_can_publish_after_preview(client):
page = EditorialPage.objects.create(slug="home", page_kind="sections")
editor = staff("editor", "content_editor")
publisher = staff("publisher", "content_publisher")
client.force_login(editor)
client.post(reverse("admin:content_editorialpage_draft", args=[page.pk]), home_post())
revision = PageRevision.objects.get(page=page)
client.force_login(publisher)
response = client.post(reverse("admin:content_editorialpage_publish", args=[page.pk, revision.pk]), {"expected_published_id": ""}, follow=True)
assert response.status_code == 200
page.refresh_from_db()
assert page.published_revision_id == revision.pk
assert b"Published revision" in response.content
def test_site_admin_uses_named_contact_fields_and_rejects_bad_url(client):
site = SiteIdentity.objects.create()
editor = staff("editor", "content_editor")
client.force_login(editor)
url = reverse("admin:content_siteidentity_draft", args=[site.pk])
response = client.get(url)
assert response.status_code == 200
assert b"contact_email_url" in response.content
assert b'name="navigation"' not in response.content
data = {"official_name": "Association", "short_name": "ACE", "contact_email_url": "javascript:alert(1)", "contact_email_label": "Email"}
for position, route in enumerate(("home", "about", "events", "blog")):
data[f"nav_{route}_label"] = route
data[f"nav_{route}_position"] = position
data[f"nav_{route}_visible"] = "on"
response = client.post(url, data)
assert response.status_code == 200
assert b"contact_email_url" in response.content
assert site.revisions.count() == 0
def test_about_optional_section_can_be_hidden_and_reordered(client):
page = EditorialPage.objects.create(slug="about", page_kind="sections")
client.force_login(staff("editor", "content_editor"))
data = {"title": "About", "intro": "Introduction", "seo_title": "About", "seo_description": "Association"}
for key, heading in (("identity", "Identity"), ("mission", "Mission"), ("contact", "Contact")):
data[f"section_{key}-heading"] = heading
data["section_identity-body"] = "Official story"
data["section_mission-position"] = "2"
data["section_mission-body"] = "Our mission"
for key in ("mission", "activities", "values", "committees", "affiliations", "faq"):
data[f"entries_{key}-TOTAL_FORMS"] = "1"
data[f"entries_{key}-INITIAL_FORMS"] = "0"
data[f"entries_{key}-MIN_NUM_FORMS"] = "0"
data[f"entries_{key}-MAX_NUM_FORMS"] = "20"
response = client.post(reverse("admin:content_editorialpage_draft", args=[page.pk]), data, follow=True)
assert response.status_code == 200
revision = PageRevision.objects.get(page=page)
assert list(revision.sections.values_list("key", "position", "visible")) == [
("identity", 0, True), ("mission", 2, False), ("contact", 3, True),
]
def test_site_editor_can_save_named_links_and_preview(client):
site = SiteIdentity.objects.create()
client.force_login(staff("editor", "content_editor"))
data = {"official_name": "Association", "short_name": "ACE", "contact_email_label": "Email", "contact_email_url": "mailto:test@example.com"}
for position, route in enumerate(("home", "about", "events", "blog")):
data[f"nav_{route}_label"] = route
data[f"nav_{route}_position"] = str(position)
data[f"nav_{route}_visible"] = "on"
response = client.post(reverse("admin:content_siteidentity_draft", args=[site.pk]), data, follow=True)
assert response.status_code == 200
revision = site.revisions.get()
assert revision.contacts == [{"kind": "email", "label": "Email", "url": "mailto:test@example.com", "position": 0, "visible": False}]
assert site.published_revision_id is None
preview = client.get(reverse("admin:content_siteidentity_preview", args=[site.pk, revision.pk]))
assert preview.status_code == 200 and b"test@example.com" in preview.content
def test_anonymous_cannot_preview_draft(client):
page = EditorialPage.objects.create(slug="home", page_kind="sections")
client.force_login(staff("editor", "content_editor"))
client.post(reverse("admin:content_editorialpage_draft", args=[page.pk]), home_post())
revision = page.revisions.get()
client.logout()
response = client.get(reverse("admin:content_editorialpage_preview", args=[page.pk, revision.pk]))
assert response.status_code == 302 and "/admin/login/" in response["Location"]
def test_staff_without_content_permission_cannot_preview_or_publish(client):
page = EditorialPage.objects.create(slug="home", page_kind="sections")
client.force_login(staff("editor", "content_editor"))
client.post(reverse("admin:content_editorialpage_draft", args=[page.pk]), home_post())
revision = page.revisions.get()
plain_staff = User.objects.create_user(username="plain", mobile="09121111111", password="TestPass123!", is_staff=True)
client.force_login(plain_staff)
assert client.get(reverse("admin:content_editorialpage_preview", args=[page.pk, revision.pk])).status_code == 403
assert client.post(reverse("admin:content_editorialpage_publish", args=[page.pk, revision.pk]), {"expected_published_id": ""}).status_code == 403
def test_stale_admin_draft_post_reports_conflict(client):
page = EditorialPage.objects.create(slug="home", page_kind="sections")
client.force_login(staff("editor", "content_editor"))
url = reverse("admin:content_editorialpage_draft", args=[page.pk])
assert client.get(url).status_code == 200
client.post(url, home_post())
response = client.post(url, home_post())
assert response.status_code == 200
assert b"newer revision" in response.content
assert page.revisions.count() == 1
def test_media_approval_is_publisher_only_and_locks_asset(client, settings, tmp_path):
settings.MEDIA_ROOT = tmp_path
buffer = io.BytesIO()
Image.new("RGB", (20, 20)).save(buffer, format="PNG")
client.force_login(staff("editor", "content_editor"))
upload = SimpleUploadedFile("logo.png", buffer.getvalue(), content_type="image/png")
response = client.post(reverse("admin:content_mediaasset_add"), {
"file": upload, "alt": "Association logo", "source": "Owner", "license": "Owned", "approved": "on", "_save": "Save",
})
assert response.status_code == 302
asset = MediaAsset.objects.get()
assert not asset.approved
client.force_login(staff("publisher", "content_publisher"))
change_url = reverse("admin:content_mediaasset_change", args=[asset.pk])
response = client.post(change_url, {
"alt": asset.alt, "source": asset.source, "license": asset.license, "approved": "on", "_save": "Save",
})
assert response.status_code == 302
asset.refresh_from_db()
assert asset.approved
assert client.get(change_url).status_code == 200
assert client.post(change_url, {"approved": ""}).status_code == 403
def test_listing_intro_page_saves_without_sections(client):
page = EditorialPage.objects.create(slug="events", page_kind="listing_intro")
client.force_login(staff("editor", "content_editor"))
response = client.post(reverse("admin:content_editorialpage_draft", args=[page.pk]), {
"title": "Events", "intro": "Find upcoming events", "seo_title": "Events", "seo_description": "Association events",
}, follow=True)
assert response.status_code == 200
revision = page.revisions.get()
assert revision.intro == "Find upcoming events" and revision.sections.count() == 0
def test_admin_restore_creates_new_published_revision(client):
page = EditorialPage.objects.create(slug="home", page_kind="sections")
client.force_login(staff("publisher", "content_publisher"))
draft_url = reverse("admin:content_editorialpage_draft", args=[page.pk])
client.post(draft_url, home_post() | {"title": "First"})
first = page.revisions.get()
client.post(reverse("admin:content_editorialpage_publish", args=[page.pk, first.pk]), {"expected_published_id": ""})
client.post(draft_url, home_post() | {"title": "Second", "base_revision_id": str(first.pk)})
second = page.revisions.order_by("-number").first()
client.post(reverse("admin:content_editorialpage_publish", args=[page.pk, second.pk]), {"expected_published_id": str(first.pk)})
response = client.post(reverse("admin:content_editorialpage_restore", args=[page.pk, first.pk]), {"expected_published_id": str(second.pk)}, follow=True)
assert response.status_code == 200
page.refresh_from_db()
assert page.published_revision.number == 3 and page.published_revision.title == "First"