from http import HTTPStatus from django.conf import settings from django.contrib.sites.models import Site from django.test import SimpleTestCase, TestCase from django.urls import reverse, set_urlconf from django.utils.translation import activate, gettext as _ from django_hosts.resolvers import reverse as reverse_with_host from djangoproject.urls import www as www_urls from releases.models import Release from ..models import Document, DocumentRelease from ..search import DocumentationCategory from ..sitemaps import DocsSitemap class RedirectsTests(SimpleTestCase): @classmethod def tearDownClass(cls): # cleanup URLconfs changed by django-hosts set_urlconf(None) super().tearDownClass() def test_team_url(self): # This URL is linked from the docs. self.assertEqual( "/foundation/teams/", reverse("members:teams", urlconf=www_urls) ) def test_internals_team(self): response = self.client.get( "/en/dev/internals/team/", headers={"host": "docs.djangoproject.localhost:8000"}, ) self.assertRedirects( response, "https://www.djangoproject.com/foundation/teams/", status_code=HTTPStatus.MOVED_PERMANENTLY, fetch_redirect_response=False, ) class SearchFormTestCase(TestCase): fixtures = ["doc_test_fixtures"] @classmethod def setUpTestData(cls): # We need to create an extra Site because docs have SITE_ID=2 Site.objects.create(name="Django test", domain="example2.com") cls.release = Release.objects.create(version="5.1") cls.doc_release = DocumentRelease.objects.create(release=cls.release) cls.active_filter = '' for category in DocumentationCategory: Document.objects.create( **{ "metadata": { "body": "Generic Views", "breadcrumbs": [ {"path": category.value, "title": str(category.label)}, ], "parents": category.value, "slug": "generic-views", "title": "Generic views", "toc": ( '\n" ), }, "path": f"{category.value}/generic-views", "release": cls.doc_release, "title": "Generic views", } ) @classmethod def tearDownClass(cls): # cleanup URLconfs changed by django-hosts set_urlconf(None) super().tearDownClass() def test_empty_get(self): response = self.client.get( "/en/dev/search/", headers={"host": "docs.djangoproject.localhost:8000"} ) self.assertEqual(response.status_code, 200) # No header item is active. self.assertNotContains(response, '
  • ') # The search result page does not have the Documentation banner. self.assertNotContains(response, '
    ') def test_search_type_filter_all(self): response = self.client.get( "/en/5.1/search/?q=generic", headers={"host": "docs.djangoproject.localhost:8000"}, ) self.assertEqual(response.status_code, 200) self.assertContains(response, "5 results for generic", html=True) self.assertContains(response, self.active_filter, count=1) self.assertContains(response, f"{self.active_filter}All", html=True) def test_search_type_filter_by_doc_types(self): for category in DocumentationCategory: with self.subTest(category=category): response = self.client.get( f"/en/5.1/search/?q=generic&category={category.value}", headers={"host": "docs.djangoproject.localhost:8000"}, ) self.assertEqual(response.status_code, 200) self.assertContains( response, "1 result for generic", html=True, ) self.assertContains(response, self.active_filter, count=1) self.assertContains( response, f"{self.active_filter}{category.label}", html=True ) self.assertContains(response, 'All', html=True) def test_search_category_filter_invalid_doc_categories(self): response = self.client.get( "/en/5.1/search/?q=generic&category=invalid-so-ignored", headers={"host": "docs.djangoproject.localhost:8000"}, ) self.assertEqual(response.status_code, 200) self.assertContains(response, "5 results for generic", html=True) self.assertContains(response, self.active_filter, count=1) self.assertContains(response, f"{self.active_filter}All", html=True) def test_search_category_filter_no_results(self): response = self.client.get( "/en/5.1/search/?q=potato&category=ref", headers={"host": "docs.djangoproject.localhost:8000"}, ) self.assertEqual(response.status_code, 200) self.assertContains(response, self.active_filter, count=1) self.assertContains( response, f"{self.active_filter}API Reference", html=True ) self.assertContains(response, "0 results for potato", html=True) self.assertContains( response, 'Please try searching all results.', html=True, ) def test_search_website_category_french(self): DocumentRelease.objects.create(release=self.release, lang="fr") response = self.client.get( "/fr/5.1/search/?q=potato&category=website", headers={"host": "docs.djangoproject.localhost:8000"}, ) self.assertEqual(response.status_code, 200) self.assertContains(response, self.active_filter, count=1) activate("fr") self.assertContains( response, f"{self.active_filter}{_('Django Website')}", html=True ) self.assertContains( response, _("The website content can only be searched in English."), html=True, ) def test_search_category_filter_preserved(self): response = self.client.get( "/en/5.1/search/?q=potato&category=ref", headers={"host": "docs.djangoproject.localhost:8000"}, ) self.assertEqual(response.status_code, 200) self.assertContains( response, '' ) self.assertContains( response, f"{self.active_filter}API Reference", html=True ) response = self.client.post( "/en/5.1/search/?q=potato&category=ref", headers={"host": "docs.djangoproject.localhost:8000"}, data={"category": "ref", "q": "fish"}, ) self.assertEqual(response.status_code, 200) self.assertContains( response, '' ) self.assertContains( response, f"{self.active_filter}API Reference", html=True ) def test_code_links(self): queryset_data = { "metadata": { "body": ( "QuerySet API Reference QuerySet select_related selects related" " things select_for_update selects things for update." ), "python_objects": { "QuerySet": "django.db.models.query.QuerySet", "QuerySet.select_related": ( "django.db.models.query.QuerySet.select_related" ), "QuerySet.select_for_update": ( "django.db.models.query.QuerySet.select_for_update" ), }, "python_objects_search": ("QuerySet select_related select_for_update"), "breadcrumbs": [{"path": "refs", "title": "API Reference"}], "parents": "API Reference", "slug": "query", "title": "QuerySet API Reference", "toc": ( '\n" ), }, "path": "refs/query", "release": self.doc_release, "title": "QuerySet", } empty_page_data = { "metadata": { "body": "Empty page", "breadcrumbs": [{"path": "refs", "title": "API Reference"}], "parents": "API Reference", "slug": "empty", "title": "Empty page", "toc": ( '\n" ), }, "path": "refs/empty", "release": self.doc_release, "title": "Empty page", } Document.objects.bulk_create( [Document(**queryset_data), Document(**empty_page_data)] ) base_url = reverse_with_host( "document-detail", host="docs", kwargs={ "lang": settings.DEFAULT_LANGUAGE_CODE, "version": "5.1", "url": "refs/query", }, ) for query, expected_code_links in [ ( "queryset", f'", ), ( "select", f'', ), ]: with self.subTest(query=query): response = self.client.get( f"/en/5.1/search/?q={query}", headers={"host": "docs.djangoproject.localhost:8000"}, ) self.assertEqual(response.status_code, 200) self.assertContains( response, f"1 result for {query}", html=True, ) self.assertContains(response, expected_code_links, html=True) class SitemapTests(TestCase): fixtures = ["doc_test_fixtures"] @classmethod def tearDownClass(cls): # cleanup URLconfs changed by django-hosts set_urlconf(None) super().tearDownClass() def test_sitemap_index(self): response = self.client.get( "/sitemap.xml", headers={"host": "docs.djangoproject.localhost:8000"} ) self.assertContains(response, "", count=2) en_sitemap_url = reverse_with_host( "document-sitemap", host="docs", kwargs={"section": settings.DEFAULT_LANGUAGE_CODE}, ) self.assertContains(response, f"{en_sitemap_url}") def test_sitemap(self): doc_release = DocumentRelease.objects.create() document = Document.objects.create( release=doc_release, metadata={"parents": DocumentationCategory.TOPICS}, ) Document.objects.create( release=doc_release, metadata={"parents": DocumentationCategory.WEBSITE}, path="example", ) sitemap = DocsSitemap(settings.DEFAULT_LANGUAGE_CODE) urls = sitemap.get_urls() self.assertEqual(len(urls), 1) url_info = urls[0] self.assertEqual(url_info["location"], document.get_absolute_url()) def test_sitemap_404(self): response = self.client.get( "/sitemap-xx.xml", headers={"host": "docs.djangoproject.localhost:8000"} ) self.assertEqual(response.status_code, 404) self.assertEqual( response.context["exception"], "No sitemap available for section: 'xx'" ) class SecurityTxtTests(TestCase): """Tests for the security.txt file.""" def test_security_txt(self): """The security.txt file should be reachable at the expected URL.""" response = self.client.get("/.well-known/security.txt") self.assertEqual(response.status_code, HTTPStatus.OK) self.assertEqual(response["Content-Type"], "text/plain") self.assertIn("Expires:", response.content.decode())