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") def test_about_role_heading_uses_shared_admin_setting(self): section = AboutSection.objects.create(section_type=AboutSection.TYPE_PEOPLE, title="Our people") AboutSectionItem.objects.create(section=section, title="Team lead", role="Leader") branding = SiteBranding.load() branding.lead_group_label = "Leadership" branding.save(update_fields=["lead_group_label"]) response = self.client.get(reverse("pages:about")) self.assertContains(response, "Leadership") self.assertNotContains(response, "Lead / Leader") 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}", )