From e28dbe7d3acd30d5b916bd394e07951662a8ce4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Braghi=C8=99?= Date: Thu, 6 Feb 2025 09:05:45 +0000 Subject: [PATCH] Add tests --- cms/articles/tests/factories.py | 4 +- cms/articles/tests/test_models.py | 4 +- cms/core/tests/test_utils.py | 134 ++++++++++++++++++++++++++++ cms/core/utils.py | 6 +- cms/topics/blocks.py | 3 +- cms/topics/tests/__init__.py | 0 cms/topics/tests/factories.py | 20 ++++- cms/topics/tests/test_blocks.py | 106 ++++++++++++++++++++++ cms/topics/tests/test_models.py | 142 ++++++++++++++++++++++++++++++ 9 files changed, 409 insertions(+), 10 deletions(-) create mode 100644 cms/core/tests/test_utils.py create mode 100644 cms/topics/tests/__init__.py create mode 100644 cms/topics/tests/test_blocks.py create mode 100644 cms/topics/tests/test_models.py diff --git a/cms/articles/tests/factories.py b/cms/articles/tests/factories.py index c5fc788c..827e0761 100644 --- a/cms/articles/tests/factories.py +++ b/cms/articles/tests/factories.py @@ -18,7 +18,7 @@ class HeadlineFigureBlockFactory(wagtail_factories.StructBlockFactory): trend = wagtail_factories.CharBlockFactory() -class ArticleSeriesFactory(wagtail_factories.PageFactory): +class ArticleSeriesPageFactory(wagtail_factories.PageFactory): """Factory for ArticleSeriesPage.""" class Meta: @@ -36,7 +36,7 @@ class Meta: django_get_or_create: ClassVar[list[str]] = ["slug", "parent"] title = factory.Faker("sentence", nb_words=4) - parent = factory.SubFactory(ArticleSeriesFactory) + parent = factory.SubFactory(ArticleSeriesPageFactory) summary = factory.Faker("text", max_nb_chars=100) news_headline = factory.Faker("text", max_nb_chars=50) diff --git a/cms/articles/tests/test_models.py b/cms/articles/tests/test_models.py index 30525746..43c56335 100644 --- a/cms/articles/tests/test_models.py +++ b/cms/articles/tests/test_models.py @@ -7,7 +7,7 @@ from django.utils.formats import date_format from wagtail.test.utils import WagtailTestUtils -from cms.articles.tests.factories import ArticleSeriesFactory, StatisticalArticlePageFactory +from cms.articles.tests.factories import ArticleSeriesPageFactory, StatisticalArticlePageFactory from cms.core.tests.factories import ContactDetailsFactory @@ -16,7 +16,7 @@ class ArticleSeriesTestCase(WagtailTestUtils, TestCase): @classmethod def setUpTestData(cls): - cls.series = ArticleSeriesFactory() + cls.series = ArticleSeriesPageFactory() def test_index_redirect_404_with_no_subpages(self): """Test index path redirects to latest.""" diff --git a/cms/core/tests/test_utils.py b/cms/core/tests/test_utils.py new file mode 100644 index 00000000..399c69ca --- /dev/null +++ b/cms/core/tests/test_utils.py @@ -0,0 +1,134 @@ +from datetime import datetime + +from django.test import TestCase +from django.utils.formats import date_format + +from cms.core.models.base import BasePage +from cms.core.utils import get_formatted_pages_list + + +# DummyPage mimics the minimum attributes and methods of a Wagtail Page. +class DummyPage(BasePage): + def __init__(self, title, summary="", listing_summary="", url="https://ons.gov.uk", **kwargs): # pylint: disable=super-init-not-called + # this just set attributes manually. + self.title = title + self.summary = summary + self.listing_summary = listing_summary + self._url = url + self._release_date = kwargs.get("release_date") + + def get_url(self, request=None, current_site=None): + return self._url + + @property + def release_date(self): + return self._release_date + + class Meta: + abstract = True + + +class DummyPageWithNoReleaseDate(DummyPage): + @property + def label(self): + return "Dummy Page" + + class Meta: + abstract = True + + +class GetFormattedPagesListTests(TestCase): + def test_without_release_date_and_listing_summary(self): + # When no listing_summary and release_date, should use summary for description, + # and use the default label. + page = DummyPage(title="Test Page", summary="Test summary", listing_summary="") + result = get_formatted_pages_list([page]) + expected = { + "title": {"text": "Test Page", "url": "https://ons.gov.uk"}, + "metadata": {"object": {"text": "Page"}}, + "description": "Test summary", + } + self.assertEqual(len(result), 1) + self.assertDictEqual(result[0], expected) + + def test_with_listing_summary_overrides_summary(self): + # When listing_summary is provided, that should be used as description. + page = DummyPage(title="Test Page", summary="Test summary", listing_summary="Listing summary") + result = get_formatted_pages_list([page]) + expected = { + "title": {"text": "Test Page", "url": "https://ons.gov.uk"}, + "metadata": {"object": {"text": "Page"}}, + "description": "Listing summary", + } + self.assertEqual(len(result), 1) + self.assertDictEqual(result[0], expected) + + def test_with_custom_label(self): + # When a custom label is defined, it should be used in metadata. + page = DummyPageWithNoReleaseDate(title="Test Page", summary="Test summary", listing_summary="") + result = get_formatted_pages_list([page]) + expected = { + "title": {"text": "Test Page", "url": "https://ons.gov.uk"}, + "metadata": {"object": {"text": "Dummy Page"}}, + "description": "Test summary", + } + self.assertEqual(len(result), 1) + self.assertDictEqual(result[0], expected) + + def test_with_release_date(self): + # When release_date is provided, metadata should include date formatting. + test_date = datetime(2024, 1, 1, 12, 30) + page = DummyPage(title="Test Page", summary="Test summary", listing_summary="", release_date=test_date) + result = get_formatted_pages_list([page]) + + expected_iso = date_format(test_date, "c") + expected_short = date_format(test_date, "DATE_FORMAT") + + expected = { + "title": {"text": "Test Page", "url": "https://ons.gov.uk"}, + "metadata": { + "object": {"text": "Page"}, + "date": { + "prefix": "Released", + "showPrefix": True, + "iso": expected_iso, + "short": expected_short, + }, + }, + "description": "Test summary", + } + self.assertEqual(len(result), 1) + self.assertDictEqual(result[0], expected) + + def test_multiple_pages(self): + # Test processing multiple dummy pages + test_date = datetime(2024, 1, 1, 12, 30) + page1 = DummyPage(title="Page One", summary="Summary One", listing_summary="", release_date=test_date) + page2 = DummyPageWithNoReleaseDate(title="Page Two", summary="Summary Two", listing_summary="Listing Two") + pages = [page1, page2] + result = get_formatted_pages_list(pages) + + expected_iso = date_format(test_date, "c") + expected_short = date_format(test_date, "DATE_FORMAT") + + expected_page1 = { + "title": {"text": "Page One", "url": "https://ons.gov.uk"}, + "metadata": { + "object": {"text": "Page"}, + "date": { + "prefix": "Released", + "showPrefix": True, + "iso": expected_iso, + "short": expected_short, + }, + }, + "description": "Summary One", + } + expected_page2 = { + "title": {"text": "Page Two", "url": "https://ons.gov.uk"}, + "metadata": {"object": {"text": "Dummy Page"}}, + "description": "Listing Two", + } + self.assertEqual(len(result), 2) + self.assertDictEqual(result[0], expected_page1) + self.assertDictEqual(result[1], expected_page2) diff --git a/cms/core/utils.py b/cms/core/utils.py index e9cc0f13..88354b9e 100644 --- a/cms/core/utils.py +++ b/cms/core/utils.py @@ -34,12 +34,12 @@ def get_formatted_pages_list( }, "description": getattr(page, "listing_summary", "") or getattr(page, "summary", ""), } - if hasattr(page, "release_date"): + if release_date := page.release_date: datum["metadata"]["date"] = { "prefix": _("Released"), "showPrefix": True, - "iso": date_format(page.release_date, "c"), - "short": date_format(page.release_date, "DATE_FORMAT"), + "iso": date_format(release_date, "c"), + "short": date_format(release_date, "DATE_FORMAT"), } data.append(datum) return data diff --git a/cms/topics/blocks.py b/cms/topics/blocks.py index 886417d8..8dd488c9 100644 --- a/cms/topics/blocks.py +++ b/cms/topics/blocks.py @@ -62,8 +62,7 @@ def get_formatted_value(self, value: "StructValue", context: dict | None = None) }, "description": value["description"] or getattr(page, "listing_summary", "") or getattr(page, "summary", ""), } - image = value["thumbnail"] or page.listing_image - if image: + if image := (value["thumbnail"] or getattr(page, "listing_image", None)): renditions = image.get_renditions("fill-144x100", "fill-288x200") formatted_value["thumbnail"] = { "smallSrc": renditions["fill-144x100"].url, diff --git a/cms/topics/tests/__init__.py b/cms/topics/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/cms/topics/tests/factories.py b/cms/topics/tests/factories.py index b03590ed..19b14e93 100644 --- a/cms/topics/tests/factories.py +++ b/cms/topics/tests/factories.py @@ -2,7 +2,7 @@ import wagtail_factories from cms.themes.tests.factories import ThemePageFactory -from cms.topics.models import TopicPage +from cms.topics.models import TopicPage, TopicPageRelatedArticle, TopicPageRelatedMethodology class TopicPageFactory(wagtail_factories.PageFactory): @@ -14,3 +14,21 @@ class Meta: title = factory.Faker("sentence", nb_words=4) summary = factory.Faker("text", max_nb_chars=100) parent = factory.SubFactory(ThemePageFactory) + + +class TopicPageRelatedArticleFactory(factory.django.DjangoModelFactory): + class Meta: + model = TopicPageRelatedArticle + + parent = factory.SubFactory(TopicPageFactory) + page = factory.SubFactory("cms.articles.tests.factories.StatisticalArticlePageFactory") + sort_order = factory.Sequence(lambda n: n) + + +class TopicPageRelatedMethodologyFactory(factory.django.DjangoModelFactory): + class Meta: + model = TopicPageRelatedMethodology + + parent = factory.SubFactory(TopicPageFactory) + page = factory.SubFactory("cms.methodology.tests.factories.MethodologyPageFactory") + sort_order = factory.Sequence(lambda n: n) diff --git a/cms/topics/tests/test_blocks.py b/cms/topics/tests/test_blocks.py new file mode 100644 index 00000000..ed8eaea4 --- /dev/null +++ b/cms/topics/tests/test_blocks.py @@ -0,0 +1,106 @@ +from django.test import TestCase +from wagtail.images.tests.utils import get_test_image_file + +from cms.home.models import HomePage +from cms.images.models import CustomImage +from cms.themes.tests.factories import ThemePageFactory +from cms.topics.blocks import ExploreMoreExternalLinkBlock, ExploreMoreInternalLinkBlock, ExploreMoreStoryBlock +from cms.topics.tests.factories import TopicPageFactory + + +class ExploreMoreBlocksTestCase(TestCase): + @classmethod + def setUpTestData(cls): + cls.home_page = HomePage.objects.first() + cls.theme_page = ThemePageFactory(listing_summary="Theme summary") + cls.topic_page = TopicPageFactory(parent=cls.theme_page, live=False) + + cls.image = CustomImage.objects.create(title="Test Image", file=get_test_image_file()) + + def test_external_link_block__get_formatted_value(self): + block = ExploreMoreExternalLinkBlock() + value = block.to_python( + { + "url": "https://ons.gov.uk", + "title": "External Link", + "description": "Test description", + "thumbnail": self.image.pk, + } + ) + + formatted = block.get_formatted_value(value) + + self.assertEqual(formatted["title"]["text"], "External Link") + self.assertEqual(formatted["title"]["url"], "https://ons.gov.uk") + self.assertEqual(formatted["description"], "Test description") + self.assertIn("smallSrc", formatted["thumbnail"]) + self.assertIn("largeSrc", formatted["thumbnail"]) + + def test_internal_link_block__get_formatted_value_with_overrides(self): + block = ExploreMoreInternalLinkBlock() + value = block.to_python( + { + "page": self.home_page.pk, + "title": "Custom Title", + "description": "Custom Description", + "thumbnail": self.image.pk, + } + ) + + formatted = block.get_formatted_value(value) + + self.assertEqual(formatted["title"]["text"], "Custom Title") + self.assertEqual(formatted["description"], "Custom Description") + self.assertIn("smallSrc", formatted["thumbnail"]) + + def test_internal_link_block__get_formatted_value_without_overrides(self): + block = ExploreMoreInternalLinkBlock() + value = block.to_python({"page": self.home_page.pk, "title": "", "description": "", "thumbnail": None}) + + # Add page attributes that would normally exist + self.home_page.listing_summary = "Page listing summary" + self.home_page.listing_image = self.image + self.home_page.save() + + formatted = block.get_formatted_value(value) + + self.assertEqual(formatted["title"]["text"], self.home_page.title) + self.assertEqual(formatted["description"], "Page listing summary") + self.assertIn("smallSrc", formatted["thumbnail"]) + + def test_internal_link_block__get_formatted_value_with_unpublished_page_returns_empty(self): + block = ExploreMoreInternalLinkBlock() + value = block.to_python({"page": self.topic_page.pk}) + + self.assertEqual(block.get_formatted_value(value), {}) + + def test_explore_more_storyblock__get_context(self): + block = ExploreMoreStoryBlock() + + # Create stream value with valid and invalid items + stream_value = block.to_python( + [ + { + "type": "external_link", + "value": { + "url": "https://ons.gov.uk", + "title": "External", + "description": "Test", + "thumbnail": self.image.pk, + }, + }, + {"type": "internal_link", "value": {"page": self.theme_page.pk}}, + {"type": "internal_link", "value": {"page": self.topic_page.pk}}, + ] + ) + + context = block.get_context(stream_value) + formatted_items = context["formatted_items"] + + # Should only contain the valid external link + self.assertEqual(len(formatted_items), 2) + self.assertEqual(formatted_items[0]["title"]["text"], "External") + self.assertIn("thumbnail", formatted_items[0]) + + self.assertEqual(formatted_items[1]["title"]["text"], self.theme_page.title) + self.assertEqual(formatted_items[1]["description"], self.theme_page.listing_summary) diff --git a/cms/topics/tests/test_models.py b/cms/topics/tests/test_models.py new file mode 100644 index 00000000..29477d31 --- /dev/null +++ b/cms/topics/tests/test_models.py @@ -0,0 +1,142 @@ +from datetime import datetime + +from django.test import TestCase, override_settings +from django.utils.translation import gettext_lazy as _ +from wagtail.coreutils import get_dummy_request + +from cms.articles.tests.factories import ArticleSeriesPageFactory, StatisticalArticlePageFactory +from cms.home.models import HomePage +from cms.methodology.tests.factories import MethodologyPageFactory +from cms.topics.tests.factories import ( + TopicPageFactory, + TopicPageRelatedArticleFactory, + TopicPageRelatedMethodologyFactory, +) + + +class TopicPageTestCase(TestCase): + @classmethod + def setUpTestData(cls): + cls.home_page = HomePage.objects.first() + cls.topic_page = TopicPageFactory(title="Test Topic") + + # Create relevant pages + cls.article_series = ArticleSeriesPageFactory(title="Article Series", parent=cls.topic_page) + cls.older_article = StatisticalArticlePageFactory( + title="Older Article", parent=cls.article_series, release_date=datetime(2024, 11, 1) + ) + cls.article = StatisticalArticlePageFactory( + title="Article", parent=cls.article_series, release_date=datetime(2024, 12, 1) + ) + + cls.topic_page.featured_series = cls.article_series + cls.topic_page.save() + + cls.methodology = MethodologyPageFactory(parent=cls.topic_page, publication_date=datetime(2024, 6, 1)) + cls.another_methodology = MethodologyPageFactory(parent=cls.topic_page, publication_date=datetime(2024, 11, 1)) + + def test_topic_label(self): + self.assertEqual(self.topic_page.label, "Topic") + + def test_latest_article_in_featured_series(self): + self.assertEqual(self.topic_page.latest_article_in_featured_series, self.article) + + another_article = StatisticalArticlePageFactory(parent=self.article_series, release_date=datetime(2025, 2, 1)) + del self.topic_page.latest_article_in_featured_series + self.assertEqual(self.topic_page.latest_article_in_featured_series, another_article) + + def test_processed_articles_combines_highlighted_and_latest_in_series(self): + # Create additional articles + article_in_other_series = StatisticalArticlePageFactory( + title="Article in other series", + parent=ArticleSeriesPageFactory(parent=self.topic_page), + release_date=datetime(2025, 2, 1), + ) + + TopicPageRelatedArticleFactory(parent=self.topic_page, page=self.older_article) + self.assertListEqual( + self.topic_page.processed_articles, [self.older_article, article_in_other_series, self.article] + ) + + def test_processed_articles_combines_highlighted_and_latest_in_series_but_not_if_same(self): + # Create additional articles + article_in_other_series = StatisticalArticlePageFactory( + title="Article in other series", + parent=ArticleSeriesPageFactory(parent=self.topic_page), + release_date=datetime(2025, 2, 1), + ) + + TopicPageRelatedArticleFactory(parent=self.topic_page, page=self.article) + self.assertListEqual(self.topic_page.processed_articles, [self.article, article_in_other_series]) + + def test_processed_articles_shows_only_highlighted_if_all_selected(self): + # Create additional articles + new_article = StatisticalArticlePageFactory(parent=self.article_series) + StatisticalArticlePageFactory( + title="Article in other series", + parent=ArticleSeriesPageFactory(parent=self.topic_page), + release_date=datetime(2025, 2, 1), + ) + + TopicPageRelatedArticleFactory(parent=self.topic_page, page=self.older_article) + TopicPageRelatedArticleFactory(parent=self.topic_page, page=new_article) + TopicPageRelatedArticleFactory(parent=self.topic_page, page=self.article) + self.assertListEqual(self.topic_page.processed_articles, [self.older_article, new_article, self.article]) + + def test_processed_methodologies_combines_highlighted_and_child_pages(self): + self.assertListEqual(self.topic_page.processed_methodologies, [self.another_methodology, self.methodology]) + + del self.topic_page.processed_methodologies + TopicPageRelatedMethodologyFactory(parent=self.topic_page, page=self.methodology) + + self.assertListEqual(self.topic_page.processed_methodologies, [self.methodology, self.another_methodology]) + + def test_processed_methodologies_shows_only_highlighted_if_all_selected(self): + new_methodology = MethodologyPageFactory(parent=self.topic_page, publication_date=datetime(2024, 2, 1)) + new_methodology2 = MethodologyPageFactory(parent=self.topic_page, publication_date=datetime(2023, 2, 1)) + + TopicPageRelatedMethodologyFactory(parent=self.topic_page, page=self.methodology) + TopicPageRelatedMethodologyFactory(parent=self.topic_page, page=new_methodology2) + TopicPageRelatedMethodologyFactory(parent=self.topic_page, page=new_methodology) + + self.assertListEqual( + self.topic_page.processed_methodologies, [self.methodology, new_methodology2, new_methodology] + ) + + def test_table_of_contents_includes_all_sections(self): + self.assertListEqual( + self.topic_page.table_of_contents, + [ + {"url": "#featured", "text": "Featured"}, + {"url": "#related-articles", "text": "Related articles"}, + {"url": "#related-methods", "text": "Methods and quality information"}, + ], + ) + + def test_table_of_contents_without_features(self): + self.topic_page.featured_series = None + + self.assertNotIn({"url": "#featured", "text": "Featured"}, self.topic_page.table_of_contents) + + def test_table_of_contents_includes_explore_more(self): + self.topic_page.explore_more = [("external_link", {"url": "https://example.com"})] + + toc = self.topic_page.table_of_contents + self.assertIn({"url": "#explore-more", "text": _("Explore more")}, toc) + + def test_get_context(self): + context = self.topic_page.get_context(get_dummy_request()) + + self.assertListEqual(context["table_of_contents"], self.topic_page.table_of_contents) + self.assertEqual(context["featured_item"], self.article) + self.assertIn("formatted_articles", context) + self.assertIn("formatted_methodologies", context) + self.assertEqual(len(context["formatted_articles"]), len(self.topic_page.processed_articles)) + self.assertEqual(len(context["formatted_methodologies"]), len(self.topic_page.processed_methodologies)) + + @override_settings(IS_EXTERNAL_ENV=True) + def test_render_in_external_env(self): + """Test that the index page renders in external environment.""" + response = self.client.get(self.topic_page.url) + + self.assertEqual(response.status_code, 200)