from django.test import TestCase
from django.urls import reverse
from django.utils import timezone
from apps.core.models import SiteBranding
from apps.pages.models import AboutSection, AboutSectionItem, BlogPost, BlogPostPartner, HomepageSection
class HomeViewTest(TestCase):
def test_home_returns_200(self):
response = self.client.get(reverse("pages:home"))
self.assertEqual(response.status_code, 200)
def test_home_uses_correct_template(self):
response = self.client.get(reverse("pages:home"))
self.assertTemplateUsed(response, "pages/home.html")
def test_section_description_renders_markdown_and_html(self):
HomepageSection.objects.create(
section_type=HomepageSection.TYPE_FEATURES,
title="Rich section",
description="**Formatted** content",
description_format="markdown",
)
response = self.client.get(reverse("pages:home"))
self.assertContains(response, "Formatted")
class AboutViewTest(TestCase):
def test_about_returns_200(self):
response = self.client.get(reverse("pages:about"))
self.assertEqual(response.status_code, 200)
def test_about_uses_correct_template(self):
response = self.client.get(reverse("pages:about"))
self.assertTemplateUsed(response, "pages/about.html")
def test_people_section_orders_roles_and_controls_read_more_per_text(self):
section = AboutSection.objects.create(
section_type=AboutSection.TYPE_PEOPLE,
badge="Our team", title="People behind the work",
content="Section introduction", read_more_content=True,
)
AboutSectionItem.objects.create(section=section, title="Advisor", role="Scientific advisor", content="Advisor profile", order=1)
AboutSectionItem.objects.create(section=section, title="Collaborator", role="Collaborator", content="Collaboration profile", read_more_content=True, order=3)
AboutSectionItem.objects.create(section=section, title="Organizer", role="Organizer", content="Organizer profile", order=9)
AboutSectionItem.objects.create(section=section, title="Leader", role="Leader", content="Leader profile", order=20)
html = self.client.get(reverse("pages:about")).content.decode()
self.assertIn("People behind the work", html)
self.assertIn("Scientific advisor", html)
self.assertLess(html.index(">Leader"), html.index(">Organizer"))
self.assertLess(html.index(">Organizer"), html.index(">Collaborator"))
self.assertLess(html.index(">Collaborator"), html.index(">Advisor"))
self.assertIn("Lead / Leader", html)
self.assertIn('data-readmore="200"', html)
self.assertEqual(html.count('data-readmore="88"'), 1)
def test_empty_people_section_is_hidden(self):
AboutSection.objects.create(section_type=AboutSection.TYPE_PEOPLE, title="Nobody yet")
response = self.client.get(reverse("pages:about"))
self.assertNotContains(response, "Nobody yet")
class BlogViewTest(TestCase):
def setUp(self):
self.post = BlogPost.objects.create(
title="Research update",
slug="research-update",
excerpt="A short summary.",
content="## Results\n\n**Promising** findings.",
content_format="markdown",
status=BlogPost.STATUS_PUBLISHED,
published_at=timezone.now(),
)
def test_blog_list_and_detail_render_published_post(self):
listing = self.client.get(reverse("pages:blog_list"))
detail = self.client.get(self.post.get_absolute_url())
self.assertContains(listing, self.post.title)
self.assertContains(detail, "
Results
")
self.assertContains(detail, "Promising")
self.assertNotContains(detail, "blog-partners")
self.assertNotContains(detail, "blog-detail-layout--with-partners")
def test_draft_post_is_not_public(self):
self.post.status = BlogPost.STATUS_DRAFT
self.post.save(update_fields=["status"])
self.assertNotContains(self.client.get(reverse("pages:blog_list")), self.post.title)
self.assertEqual(self.client.get(self.post.get_absolute_url()).status_code, 404)
def test_article_partners_render_with_roles_and_optional_details(self):
BlogPostPartner.objects.create(
post=self.post, role=BlogPostPartner.ORGANIZER, name="Research Group",
description="Project lead", image="blog/partners/group.png",
image_alt="Research Group logo", url="https://example.org",
)
BlogPostPartner.objects.create(
post=self.post, role=BlogPostPartner.COLLABORATOR, name="Care Team"
)
detail = self.client.get(self.post.get_absolute_url())
self.assertContains(detail, "Research Group")
self.assertContains(detail, "Collaborator")
self.assertContains(detail, "Research Group logo")
self.assertContains(detail, "https://example.org")
self.assertContains(detail, "Care Team")
def test_blog_read_more_is_opt_in(self):
detail = self.client.get(self.post.get_absolute_url())
self.assertNotContains(detail, 'data-readmore="600"')
self.post.read_more_content = True
self.post.save(update_fields=["read_more_content"])
detail = self.client.get(self.post.get_absolute_url())
self.assertContains(detail, 'data-readmore="600"')
def test_partners_follow_article_and_sort_by_role_then_number(self):
BlogPostPartner.objects.create(post=self.post, role=BlogPostPartner.COLLABORATOR, name="Second collaborator", order=2)
BlogPostPartner.objects.create(post=self.post, role=BlogPostPartner.COLLABORATOR, name="First collaborator", order=1)
BlogPostPartner.objects.create(post=self.post, role=BlogPostPartner.ORGANIZER, name="Organizer", order=9)
BlogPostPartner.objects.create(post=self.post, role="Lead", name="Lead", order=5)
BlogPostPartner.objects.create(post=self.post, role="Leader", name="Leader", order=1)
html = self.client.get(self.post.get_absolute_url()).content.decode()
self.assertLess(html.index('class="blog-article rich-content'), html.index('class="blog-partners"'))
self.assertLess(html.index('>Leader'), html.index('>Lead'))
self.assertLess(html.index('>Lead'), html.index('>Organizer'))
self.assertLess(html.index('>Organizer'), html.index('>First collaborator'))
self.assertLess(html.index('>First collaborator'), html.index('>Second collaborator'))
self.assertLess(html.index('Lead / Leader'), html.index('>Organizers'))
def test_partner_headings_are_shared_and_description_read_more_is_independent(self):
branding = SiteBranding.load()
branding.article_people_eyebrow = "Our team"
branding.article_people_title = "Project contributors"
branding.save(update_fields=["article_people_eyebrow", "article_people_title"])
partner = BlogPostPartner.objects.create(
post=self.post, role=BlogPostPartner.ORGANIZER,
name="Coordinator", description="A detailed profile", read_more_description=True,
)
html = self.client.get(self.post.get_absolute_url()).content.decode()
self.assertIn("Our team", html)
self.assertIn("Project contributors", html)
self.assertNotIn("The people behind this article", html)
self.assertIn('data-readmore="88"', html)
partner.read_more_description = False
partner.save(update_fields=["read_more_description"])
html = self.client.get(self.post.get_absolute_url()).content.decode()
self.assertNotIn('data-readmore="88"', html)
other_post = BlogPost.objects.create(
title="Another update", slug="another-update", content="Another article",
status=BlogPost.STATUS_PUBLISHED, published_at=timezone.now(),
)
BlogPostPartner.objects.create(post=other_post, name="Contributor")
other_html = self.client.get(other_post.get_absolute_url()).content.decode()
self.assertIn("Our team", other_html)
self.assertIn("Project contributors", other_html)
def test_custom_role_can_be_named_by_admin(self):
BlogPostPartner.objects.create(post=self.post, role="Scientific advisor", name="Advisor", order=0)
BlogPostPartner.objects.create(post=self.post, role=BlogPostPartner.COLLABORATOR, name="Partner", order=5)
BlogPostPartner.objects.create(post=self.post, role=BlogPostPartner.ORGANIZER, name="Lead", order=9)
html = self.client.get(self.post.get_absolute_url()).content.decode()
self.assertIn("Scientific advisor", html)
self.assertLess(html.index(">Lead"), html.index(">Partner"))
self.assertLess(html.index(">Partner"), html.index(">Advisor"))
class ContactViewTest(TestCase):
def test_contact_returns_200(self):
response = self.client.get(reverse("pages:contact"))
self.assertEqual(response.status_code, 200)
def test_contact_uses_correct_template(self):
response = self.client.get(reverse("pages:contact"))
self.assertTemplateUsed(response, "pages/contact.html")
class NavigationContextTest(TestCase):
def test_nav_main_products_in_context_on_all_pages(self):
urls = [
reverse("pages:home"),
reverse("pages:about"),
reverse("pages:blog_list"),
reverse("pages:contact"),
reverse("products:overview"),
]
for url in urls:
response = self.client.get(url)
self.assertIn(
"nav_main_products",
response.context,
f"Missing nav_main_products at {url}",
)
def test_nav_custom_pages_in_context_on_all_pages(self):
urls = [
reverse("pages:home"),
reverse("pages:about"),
reverse("pages:blog_list"),
reverse("pages:contact"),
]
for url in urls:
response = self.client.get(url)
self.assertIn(
"nav_custom_pages",
response.context,
f"Missing nav_custom_pages at {url}",
)