|
| 1 | +import unittest |
1 | 2 | from http import HTTPStatus |
2 | 3 |
|
3 | 4 | from django.conf import settings |
| 5 | +from django.contrib.contenttypes.models import ContentType |
4 | 6 | from django.contrib.sites.models import Site |
5 | 7 | from django.test import SimpleTestCase, TestCase |
6 | 8 | from django.urls import reverse, set_urlconf |
7 | 9 | from django.utils.translation import activate, gettext as _ |
8 | 10 | from django_hosts.resolvers import reverse as reverse_with_host, reverse_host |
9 | 11 |
|
10 | | -from djangoproject.urls import www as www_urls |
| 12 | +from djangoproject.urls import docs as docs_urls, www as www_urls |
11 | 13 | from releases.models import Release |
12 | 14 |
|
13 | 15 | from ..models import Document, DocumentRelease |
@@ -40,6 +42,146 @@ def test_internals_team(self): |
40 | 42 | fetch_redirect_response=False, |
41 | 43 | ) |
42 | 44 |
|
| 45 | + def test_redirect_index_view(self): |
| 46 | + response = self.client.get( |
| 47 | + "/en/dev/index/", # Route without name |
| 48 | + headers={"host": reverse_host("docs")}, |
| 49 | + ) |
| 50 | + self.assertRedirects(response, "/en/dev/", fetch_redirect_response=False) |
| 51 | + |
| 52 | + |
| 53 | +class LangAndReleaseRedirectTests(TestCase): |
| 54 | + @classmethod |
| 55 | + def setUpTestData(cls): |
| 56 | + cls.release = Release.objects.create(version="5.2") |
| 57 | + cls.doc_release = DocumentRelease.objects.create( |
| 58 | + release=cls.release, is_default=True |
| 59 | + ) |
| 60 | + |
| 61 | + def test_index_view_redirect_to_current_document_release(self): |
| 62 | + response = self.client.get( |
| 63 | + reverse_with_host("homepage", host="docs"), |
| 64 | + headers={"host": reverse_host("docs")}, |
| 65 | + ) |
| 66 | + self.assertRedirects( |
| 67 | + response, self.doc_release.get_absolute_url(), fetch_redirect_response=False |
| 68 | + ) |
| 69 | + |
| 70 | + def test_language_view_redirect_to_current_document_release_with_the_same_language( |
| 71 | + self, |
| 72 | + ): |
| 73 | + fr_doc_release = DocumentRelease.objects.create(release=self.release, lang="fr") |
| 74 | + response = self.client.get( |
| 75 | + "/fr/", # Route without name |
| 76 | + headers={"host": reverse_host("docs")}, |
| 77 | + ) |
| 78 | + self.assertRedirects( |
| 79 | + response, fr_doc_release.get_absolute_url(), fetch_redirect_response=False |
| 80 | + ) |
| 81 | + |
| 82 | + def test_stable_view_redirect_to_current_document_release(self): |
| 83 | + response = self.client.get( |
| 84 | + reverse_with_host( |
| 85 | + # The stable view doesn't have a name but it's basically |
| 86 | + # the document-detail route with a version set to "stable" |
| 87 | + "document-detail", |
| 88 | + kwargs={ |
| 89 | + "version": "stable", |
| 90 | + "lang": self.doc_release.lang, |
| 91 | + "url": "intro", |
| 92 | + }, |
| 93 | + host="docs", |
| 94 | + ), |
| 95 | + headers={"host": reverse_host("docs")}, |
| 96 | + ) |
| 97 | + # Using Django's `reverse()` over django-hosts's `reverse_host()` as the later |
| 98 | + # one return an absolute URL but the view redirect only using the path component |
| 99 | + expected_url = reverse( |
| 100 | + # The stable view route doesn't have a name but it's basically |
| 101 | + # the `document-detail` route with a version set to "stable" |
| 102 | + "document-detail", |
| 103 | + kwargs={ |
| 104 | + "version": self.doc_release.version, |
| 105 | + "lang": self.doc_release.lang, |
| 106 | + "url": "intro", |
| 107 | + }, |
| 108 | + urlconf=docs_urls, |
| 109 | + ) |
| 110 | + self.assertRedirects(response, expected_url, fetch_redirect_response=False) |
| 111 | + |
| 112 | + |
| 113 | +class DocumentViewTests(TestCase): |
| 114 | + @classmethod |
| 115 | + def setUpTestData(cls): |
| 116 | + cls.doc_release = DocumentRelease.objects.create(is_default=True) |
| 117 | + |
| 118 | + def test_document_index_view(self): |
| 119 | + # Set up a release so we aren't in `dev` version |
| 120 | + self.doc_release.release = Release.objects.create(version="5.2") |
| 121 | + self.doc_release.save(update_fields=["release"]) |
| 122 | + |
| 123 | + response = self.client.get( |
| 124 | + reverse_with_host( |
| 125 | + "document-index", |
| 126 | + kwargs={ |
| 127 | + "lang": self.doc_release.lang, |
| 128 | + "version": self.doc_release.version, |
| 129 | + }, |
| 130 | + host="docs", |
| 131 | + ), |
| 132 | + headers={"host": reverse_host("docs")}, |
| 133 | + ) |
| 134 | + self.assertEqual(response.status_code, 200) |
| 135 | + self.assertEqual(response.context.get("docurl"), "") |
| 136 | + self.assertEqual( |
| 137 | + response.context.get("rtd_version"), f"{self.doc_release.version}.x" |
| 138 | + ) |
| 139 | + # Check the header used for Fastly |
| 140 | + self.assertEqual(response.headers.get("Surrogate-Control"), "max-age=604800") |
| 141 | + |
| 142 | + def test_document_index_view_with_dev_version(self): |
| 143 | + response = self.client.get( |
| 144 | + reverse_with_host( |
| 145 | + "document-index", |
| 146 | + kwargs={"lang": self.doc_release.lang, "version": "dev"}, |
| 147 | + host="docs", |
| 148 | + ), |
| 149 | + headers={"host": reverse_host("docs")}, |
| 150 | + ) |
| 151 | + self.assertEqual(response.status_code, 200) |
| 152 | + self.assertEqual(response.context.get("rtd_version"), "latest") |
| 153 | + |
| 154 | + @unittest.expectedFailure |
| 155 | + def test_document_index_view_with_stable_version(self): |
| 156 | + response = self.client.get( |
| 157 | + reverse_with_host( |
| 158 | + "document-index", |
| 159 | + kwargs={"lang": self.doc_release.lang, "version": "stable"}, |
| 160 | + host="docs", |
| 161 | + ), |
| 162 | + headers={"host": reverse_host("docs")}, |
| 163 | + ) |
| 164 | + self.assertEqual(response.status_code, 200) |
| 165 | + self.assertEqual(response.context.get("rtd_version"), "latest") |
| 166 | + |
| 167 | + def test_document_detail_view(self): |
| 168 | + response = self.client.get( |
| 169 | + reverse_with_host( |
| 170 | + "document-detail", |
| 171 | + kwargs={ |
| 172 | + "lang": self.doc_release.lang, |
| 173 | + "version": "dev", |
| 174 | + "url": "intro", |
| 175 | + }, |
| 176 | + host="docs", |
| 177 | + ), |
| 178 | + headers={"host": reverse_host("docs")}, |
| 179 | + ) |
| 180 | + self.assertEqual(response.status_code, 200) |
| 181 | + self.assertEqual(response.context.get("docurl"), "intro") |
| 182 | + # Check the header used for Fastly |
| 183 | + self.assertEqual(response.headers.get("Surrogate-Control"), "max-age=604800") |
| 184 | + |
43 | 185 |
|
44 | 186 | class SearchFormTestCase(TestCase): |
45 | 187 | fixtures = ["doc_test_fixtures"] |
@@ -278,6 +420,31 @@ def test_code_links(self): |
278 | 420 | self.assertContains(response, expected_code_links, html=True) |
279 | 421 |
|
280 | 422 |
|
| 423 | +class SearchRedirectTests(TestCase): |
| 424 | + @classmethod |
| 425 | + def setUpTestData(cls): |
| 426 | + cls.doc_release = DocumentRelease.objects.create(is_default=True) |
| 427 | + |
| 428 | + def test_redirect_search_view(self): |
| 429 | + # With a `q` parameters |
| 430 | + response = self.client.get( |
| 431 | + "/search/?q=django", headers={"host": reverse_host("docs")} |
| 432 | + ) |
| 433 | + self.assertRedirects( |
| 434 | + response, |
| 435 | + "http://" + reverse_host("docs") + "/en/dev/search/?q=django", |
| 436 | + fetch_redirect_response=False, |
| 437 | + ) |
| 438 | + |
| 439 | + # Without a `q` parameters |
| 440 | + response = self.client.get("/search/", headers={"host": reverse_host("docs")}) |
| 441 | + self.assertRedirects( |
| 442 | + response, |
| 443 | + "http://" + reverse_host("docs") + "/en/dev/search/", |
| 444 | + fetch_redirect_response=False, |
| 445 | + ) |
| 446 | + |
| 447 | + |
281 | 448 | class SitemapTests(TestCase): |
282 | 449 | fixtures = ["doc_test_fixtures"] |
283 | 450 |
|
@@ -324,3 +491,104 @@ def test_sitemap_404(self): |
324 | 491 | self.assertEqual( |
325 | 492 | response.context["exception"], "No sitemap available for section: 'xx'" |
326 | 493 | ) |
| 494 | + |
| 495 | + |
| 496 | +class OpenSearchTests(TestCase): |
| 497 | + @classmethod |
| 498 | + def setUpTestData(cls): |
| 499 | + cls.doc_release = DocumentRelease.objects.create( |
| 500 | + release=Release.objects.create(version="5.2"), is_default=True |
| 501 | + ) |
| 502 | + |
| 503 | + def test_search_suggestions_view(self): |
| 504 | + # Without `q` parameter |
| 505 | + response = self.client.get( |
| 506 | + reverse_with_host( |
| 507 | + "document-search-suggestions", |
| 508 | + kwargs={ |
| 509 | + "lang": self.doc_release.lang, |
| 510 | + "version": self.doc_release.version, |
| 511 | + }, |
| 512 | + host="docs", |
| 513 | + ), |
| 514 | + headers={"host": reverse_host("docs")}, |
| 515 | + ) |
| 516 | + self.assertEqual(response.status_code, 200) |
| 517 | + self.assertEqual(response.headers["Content-Type"], "application/json") |
| 518 | + self.assertEqual(response.json(), []) |
| 519 | + |
| 520 | + # With `q` parameter but no Document |
| 521 | + response = self.client.get( |
| 522 | + reverse_with_host( |
| 523 | + "document-search-suggestions", |
| 524 | + kwargs={ |
| 525 | + "lang": self.doc_release.lang, |
| 526 | + "version": self.doc_release.version, |
| 527 | + }, |
| 528 | + host="docs", |
| 529 | + ) |
| 530 | + + "?q=test", |
| 531 | + headers={"host": reverse_host("docs")}, |
| 532 | + ) |
| 533 | + self.assertEqual(response.status_code, 200) |
| 534 | + self.assertEqual(response.headers["Content-Type"], "application/json") |
| 535 | + self.assertEqual(response.json(), ["test", [], [], []]) |
| 536 | + |
| 537 | + # # With `q` parameter and a Document |
| 538 | + document = Document.objects.create( |
| 539 | + release=self.doc_release, |
| 540 | + path="test-document", |
| 541 | + title="test title", |
| 542 | + ) |
| 543 | + response = self.client.get( |
| 544 | + reverse_with_host( |
| 545 | + "document-search-suggestions", |
| 546 | + kwargs={ |
| 547 | + "lang": self.doc_release.lang, |
| 548 | + "version": self.doc_release.version, |
| 549 | + }, |
| 550 | + host="docs", |
| 551 | + ) |
| 552 | + + "?q=test", |
| 553 | + headers={"host": reverse_host("docs")}, |
| 554 | + ) |
| 555 | + self.assertEqual(response.status_code, 200) |
| 556 | + self.assertEqual(response.headers["Content-Type"], "application/json") |
| 557 | + self.assertEqual( |
| 558 | + response.json(), |
| 559 | + [ |
| 560 | + "test", |
| 561 | + ["test title"], |
| 562 | + [], |
| 563 | + [ |
| 564 | + reverse_with_host( |
| 565 | + "contenttypes-shortcut", |
| 566 | + kwargs={ |
| 567 | + "content_type_id": ContentType.objects.get_for_model( |
| 568 | + Document |
| 569 | + ).pk, |
| 570 | + "object_id": document.id, |
| 571 | + }, |
| 572 | + ) |
| 573 | + ], |
| 574 | + ], |
| 575 | + ) |
| 576 | + |
| 577 | + def test_search_description(self): |
| 578 | + response = self.client.get( |
| 579 | + reverse_with_host( |
| 580 | + "document-search-description", |
| 581 | + kwargs={ |
| 582 | + "lang": self.doc_release.lang, |
| 583 | + "version": self.doc_release.version, |
| 584 | + }, |
| 585 | + host="docs", |
| 586 | + ), |
| 587 | + headers={"host": reverse_host("docs")}, |
| 588 | + ) |
| 589 | + self.assertEqual(response.status_code, 200) |
| 590 | + self.assertEqual( |
| 591 | + response.headers["Content-Type"], "application/opensearchdescription+xml" |
| 592 | + ) |
| 593 | + self.assertTemplateUsed("docs/search_description.html") |
| 594 | + self.assertContains(response, f"<Language>{self.doc_release.lang}</Language>") |
0 commit comments