1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
|
from http import HTTPStatus
from io import StringIO
from django.conf import settings
from django.core.management import call_command
from django.test import TestCase
from django.urls import NoReverseMatch, get_resolver
from django.utils.translation import activate, gettext as _
from django_hosts.resolvers import reverse
from docs.models import DocumentRelease, Release
class ReleaseMixin:
@classmethod
def setUpTestData(cls):
r2, _ = Release.objects.get_or_create(version="2.0")
DocumentRelease.objects.get_or_create(
is_default=True,
defaults={"lang": settings.DEFAULT_LANGUAGE_CODE, "release": r2},
)
class LocaleSmokeTests(TestCase):
"""
Smoke test a translated string from each of the 3 locale directories
(one defined in settings.LOCALE_PATHS, plus the dashboard and docs apps).
"""
def test_dashboard_locale(self):
"""dashboard/locale/ should contain translations for 'Development dashboard'"""
activate("fr")
translated = _("Development dashboard")
self.assertEqual(
translated,
"Tableau de bord de développement",
msg="dashboard/locale/ translation not loaded or incorrect",
)
def test_docs_locale(self):
"""docs/locale/ should contain translations for 'Using Django'"""
activate("fr")
translated = _("Using Django")
self.assertEqual(
translated,
"Utilisation de Django",
msg="docs/locale/ translation not loaded or incorrect",
)
def test_project_locale(self):
"""locale/ should contain translations for 'Fundraising'"""
activate("fr")
translated = _("Fundraising")
self.assertEqual(
translated,
"Levée de fonds",
msg="project-level locale/ translation not loaded or incorrect",
)
class TemplateViewTests(ReleaseMixin, TestCase):
"""
Tests for views that are instances of TemplateView.
"""
def assertView(self, name):
self.assertContains(self.client.get(reverse(name, host="www")), "django")
def test_homepage(self):
self.assertView("homepage")
def test_overview(self):
self.assertView("overview")
def test_start(self):
self.assertView("start")
def test_code_of_conduct(self):
self.assertView("code_of_conduct")
def test_conduct_faq(self):
self.assertView("conduct_faq")
def test_conduct_reporting(self):
self.assertView("conduct_reporting")
def test_conduct_enforcement(self):
self.assertView("conduct_enforcement")
def test_conduct_changes(self):
self.assertView("conduct_changes")
def test_styleguide(self):
self.assertView("styleguide")
class ExcludeHostsLocaleMiddlewareTests(ReleaseMixin, TestCase):
"""
djangoproject.middleware.ExcludeHostsLocaleMiddleware properly prevents
the hosts in settings.LOCALE_MIDDLEWARE_EXCLUDED_HOSTS from being
processed by django.middleware.locale.LocaleMiddleware, as evidenced by
the presence or absence of 'Content-Language' and 'Vary' headers in the
response.
"""
docs_host = "docs.djangoproject.localhost"
www_host = "www.djangoproject.localhost"
def test_docs_host_excluded(self):
"""We get no Content-Language or Vary headers when docs host is excluded"""
with self.settings(LOCALE_MIDDLEWARE_EXCLUDED_HOSTS=[self.docs_host]):
resp = self.client.get("/", headers={"host": self.docs_host})
self.assertEqual(resp.status_code, HTTPStatus.OK)
self.assertNotIn("Content-Language", resp)
self.assertNotIn("Vary", resp)
def test_docs_host_with_port_excluded(self):
"""
We get no Content-Language or Vary headers when docs host
(with a port) is excluded
"""
with self.settings(LOCALE_MIDDLEWARE_EXCLUDED_HOSTS=[self.docs_host]):
resp = self.client.get("/", headers={"host": "%s:8000" % self.docs_host})
self.assertEqual(resp.status_code, HTTPStatus.FOUND)
self.assertNotIn("Content-Language", resp)
self.assertNotIn("Vary", resp)
def test_docs_host_forwarded_excluded(self):
"""
We get no Content-Language or Vary headers when docs host
(via X-Forwarded_host) is excluded
"""
with self.settings(
LOCALE_MIDDLEWARE_EXCLUDED_HOSTS=[self.docs_host], USE_X_FORWARDED_HOST=True
):
resp = self.client.get("/", headers={"x-forwarded-host": self.docs_host})
self.assertEqual(resp.status_code, HTTPStatus.OK)
self.assertNotIn("Content-Language", resp)
self.assertNotIn("Vary", resp)
def test_docs_host_not_excluded(self):
"""We still get Content-Language when docs host is not excluded"""
with self.settings(LOCALE_MIDDLEWARE_EXCLUDED_HOSTS=[]):
resp = self.client.get("/", headers={"host": self.docs_host})
self.assertEqual(resp.status_code, HTTPStatus.OK)
self.assertIn("Content-Language", resp)
self.assertIn("Vary", resp)
def test_www_host(self):
"""www should still use LocaleMiddleware"""
with self.settings(LOCALE_MIDDLEWARE_EXCLUDED_HOSTS=[self.docs_host]):
resp = self.client.get("/", headers={"host": self.www_host})
self.assertEqual(resp.status_code, HTTPStatus.OK)
self.assertIn("Content-Language", resp)
self.assertIn("Vary", resp)
def test_www_host_with_port(self):
"""www (with a port) should still use LocaleMiddleware"""
with self.settings(LOCALE_MIDDLEWARE_EXCLUDED_HOSTS=[self.docs_host]):
resp = self.client.get("/", headers={"host": "%s:8000" % self.www_host})
self.assertEqual(resp.status_code, HTTPStatus.OK)
self.assertIn("Content-Language", resp)
self.assertIn("Vary", resp)
# https://adamj.eu/tech/2024/06/23/django-test-pending-migrations/
class PendingMigrationsTests(TestCase):
def test_no_pending_migrations(self):
out = StringIO()
try:
call_command(
"makemigrations",
"--check",
stdout=out,
stderr=StringIO(),
)
except SystemExit: # pragma: no cover
raise AssertionError("Pending migrations:\n" + out.getvalue()) from None
class Header1Tests(ReleaseMixin, TestCase):
def extract_patterns(self, patterns, prefix="", urls=None):
urls = urls or []
for pattern in patterns:
if hasattr(pattern, "url_patterns"):
self.extract_patterns(
pattern.url_patterns, prefix + pattern.pattern.regex.pattern
)
elif hasattr(pattern, "pattern") and pattern.name:
try:
urls.append(reverse(pattern.name))
except NoReverseMatch:
pass # Ignore URLs that require arguments.
return urls
def test_single_h1_per_page(self):
excluded_urls = [
"rss/",
"styleguide/", # Has multiple <h1> examples.
"admin/", # Admin templates are out of our control.
"reset/done/", # Uses an admin template.
"sitemap.xml",
]
resolver = get_resolver()
urls = self.extract_patterns(resolver.url_patterns)
for url in urls:
if all(url_substring not in url for url_substring in excluded_urls):
with self.subTest(url=url):
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, "<h1", count=1)
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())
class SiteMapTests(TestCase):
def test_sitemap_renders(self):
response = self.client.get(reverse("sitemap"))
self.assertEqual(response.status_code, 200)
|