Skip to content

Commit de1e90d

Browse files
committed
Many more tests
1 parent 53cb872 commit de1e90d

File tree

1 file changed

+180
-51
lines changed

1 file changed

+180
-51
lines changed
Lines changed: 180 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1+
from django.http import HttpRequest, QueryDict
2+
from django.template import Context, Template
13
from django.test import TestCase
24
from django.urls import reverse
5+
36
from webstack_django_sorting import settings
7+
from webstack_django_sorting.common import render_sort_anchor
48

59
from . import models
610

711

8-
class IndexTest(TestCase):
12+
class SortingTest(TestCase):
13+
"""Test Django template sorting functionality."""
14+
915
def setUp(self):
1016
self.url = reverse("secret_list")
11-
1217
self.foo_file = models.SecretFile.objects.create(filename="foo.txt", order=1, size=1024)
1318
self.bar_file = models.SecretFile.objects.create(filename="bar.txt", order=2, size=512)
1419

@@ -19,77 +24,201 @@ def test_list(self):
1924

2025
def test_sorting_direction(self):
2126
response = self.client.get(self.url, {"sort": "order", "dir": "asc"})
22-
values = [self.foo_file, self.bar_file]
23-
self.assertQuerySetEqual(response.context["secret_files"], values)
27+
self.assertQuerySetEqual(response.context["secret_files"], [self.foo_file, self.bar_file])
2428

2529
response = self.client.get(self.url, {"sort": "order", "dir": "desc"})
26-
values = [self.bar_file, self.foo_file]
27-
self.assertQuerySetEqual(response.context["secret_files"], values)
30+
self.assertQuerySetEqual(response.context["secret_files"], [self.bar_file, self.foo_file])
31+
32+
# Invalid direction defaults to asc
33+
response = self.client.get(self.url, {"sort": "order", "dir": "INVALID"})
34+
self.assertQuerySetEqual(response.context["secret_files"], [self.foo_file, self.bar_file])
35+
36+
def test_sorting_by_different_fields(self):
37+
"""Test sorting works on multiple field types."""
38+
response = self.client.get(self.url, {"sort": "size", "dir": "asc"})
39+
self.assertQuerySetEqual(response.context["secret_files"], [self.bar_file, self.foo_file])
2840

29-
# Nothing wrong happens with an invalid direction and ASC is used
30-
response = self.client.get(self.url, {"sort": "order", "dir": "NOT"})
31-
values = [self.foo_file, self.bar_file]
32-
self.assertQuerySetEqual(response.context["secret_files"], values)
41+
response = self.client.get(self.url, {"sort": "filename", "dir": "asc"})
42+
self.assertQuerySetEqual(response.context["secret_files"], [self.bar_file, self.foo_file])
3343

34-
def test_sorting_argument(self):
35-
# Nothing wrong happens with invalid sort argument
36-
response = self.client.get(self.url, {"sort": "NOT EXISTING"})
44+
def test_invalid_sort_field(self):
45+
"""Invalid sort field is handled gracefully."""
46+
response = self.client.get(self.url, {"sort": "NOT_EXISTING"})
3747
self.assertContains(response, "foo.txt")
3848

49+
def test_empty_queryset(self):
50+
"""Sorting an empty queryset doesn't raise errors."""
51+
models.SecretFile.objects.all().delete()
52+
response = self.client.get(self.url, {"sort": "order", "dir": "asc"})
53+
self.assertEqual(response.status_code, 200)
3954

40-
class NullsTestCase(TestCase):
41-
def setUp(self):
42-
self.nulls_first_url = reverse("nulls_first")
43-
self.nulls_last_url = reverse("nulls_last")
4455

56+
class NullsOrderingTest(TestCase):
57+
"""Test NULL value ordering."""
58+
59+
def setUp(self):
4560
self.foo_file = models.SecretFile.objects.create(filename="foo.txt", order=1, size=1024)
4661
self.bar_file = models.SecretFile.objects.create(filename="bar.txt", order=2, size=512)
4762
self.none_file = models.SecretFile.objects.create(filename=None, order=3, size=512)
4863

49-
def test_sorting_nulls_first(self):
50-
"""Verify None sorted field_name is in first places when sorting in asc and desc order"""
51-
52-
# asc order
53-
values = [self.none_file, self.bar_file, self.foo_file]
54-
response = self.client.get(
55-
self.nulls_first_url, {"sort": "filename", "nulls": "first", "dir": "asc"}
64+
def test_nulls_first(self):
65+
"""NULL values appear first when nulls=first."""
66+
url = reverse("nulls_first")
67+
response = self.client.get(url, {"sort": "filename", "nulls": "first", "dir": "asc"})
68+
self.assertQuerySetEqual(
69+
list(response.context["secret_files"]),
70+
[self.none_file, self.bar_file, self.foo_file],
5671
)
57-
self.assertQuerySetEqual(list(response.context["secret_files"]), values)
5872

59-
# desc order
60-
values = [self.none_file, self.foo_file, self.bar_file]
61-
response = self.client.get(
62-
self.nulls_first_url,
63-
{"sort": "filename", "nulls": "first", "dir": "desc"},
73+
def test_nulls_last(self):
74+
"""NULL values appear last when nulls=last."""
75+
url = reverse("nulls_last")
76+
response = self.client.get(url, {"sort": "filename", "nulls": "last", "dir": "asc"})
77+
self.assertQuerySetEqual(
78+
list(response.context["secret_files"]),
79+
[self.bar_file, self.foo_file, self.none_file],
6480
)
65-
self.assertQuerySetEqual(list(response.context["secret_files"]), values)
6681

67-
def test_sorting_nulls_last(self):
68-
"""Verify None sorted field_name is in last places when sorting in asc and desc order."""
82+
def test_invalid_nulls_param_raises_404(self):
83+
"""Invalid nulls parameter raises 404 when configured."""
84+
url = reverse("nulls_last")
85+
saved = settings.INVALID_FIELD_RAISES_404
86+
try:
87+
settings.INVALID_FIELD_RAISES_404 = True
88+
response = self.client.get(url, {"sort": "filename", "nulls": "invalid", "dir": "asc"})
89+
self.assertEqual(response.status_code, 404)
90+
finally:
91+
settings.INVALID_FIELD_RAISES_404 = saved
6992

70-
# asc order
71-
values = [self.bar_file, self.foo_file, self.none_file]
72-
response = self.client.get(
73-
self.nulls_last_url, {"sort": "filename", "nulls": "last", "dir": "asc"}
74-
)
75-
self.assertQuerySetEqual(list(response.context["secret_files"]), values)
7693

77-
# desc order
78-
values = [self.foo_file, self.bar_file, self.none_file]
94+
class Jinja2Test(TestCase):
95+
"""Test Jinja2 template support."""
96+
97+
def setUp(self):
98+
self.foo_file = models.SecretFile.objects.create(filename="foo.txt", order=1, size=1024)
99+
self.bar_file = models.SecretFile.objects.create(filename="bar.txt", order=2, size=512)
100+
101+
def test_jinja_sorting(self):
102+
"""Jinja2 templates sort correctly."""
103+
url = reverse("jinja_secret_list")
104+
105+
# Ascending
106+
response = self.client.get(url, {"sort": "order", "dir": "asc"})
107+
content = response.content.decode()
108+
self.assertLess(content.find("foo.txt"), content.find("bar.txt"))
109+
110+
# Descending
111+
response = self.client.get(url, {"sort": "order", "dir": "desc"})
112+
content = response.content.decode()
113+
self.assertGreater(content.find("foo.txt"), content.find("bar.txt"))
114+
115+
def test_jinja_nulls_ordering(self):
116+
"""Jinja2 templates handle NULL ordering."""
117+
models.SecretFile.objects.create(filename=None, order=3, size=512)
118+
79119
response = self.client.get(
80-
self.nulls_last_url, {"sort": "filename", "nulls": "last", "dir": "desc"}
120+
reverse("jinja_nulls_first"), {"sort": "filename", "nulls": "first", "dir": "asc"}
81121
)
82-
self.assertQuerySetEqual(list(response.context["secret_files"]), values)
122+
content = response.content.decode()
123+
self.assertLess(content.find("bar.txt"), content.find("foo.txt"))
124+
125+
126+
class RenderSortAnchorTest(TestCase):
127+
"""Test the render_sort_anchor function - core anchor rendering logic."""
128+
129+
def _make_request(self, query_params=None):
130+
request = HttpRequest()
131+
request.path = "/test/"
132+
request.GET = QueryDict(mutable=True)
133+
if query_params:
134+
for key, value in query_params.items():
135+
request.GET[key] = value
136+
return request
137+
138+
def test_basic_anchor(self):
139+
"""Anchor renders with correct href and title."""
140+
request = self._make_request()
141+
result = render_sort_anchor(request, "name", "Name", "asc")
142+
self.assertIn('href="/test/?sort=name&dir=asc"', result)
143+
self.assertIn('title="Name"', result)
144+
self.assertIn(">Name</a>", result)
145+
146+
def test_three_way_toggle(self):
147+
"""Test the three-way sorting toggle: none -> asc -> desc -> none."""
148+
# No sort -> asc (with asc default)
149+
result = render_sort_anchor(self._make_request(), "name", "Name", "asc")
150+
self.assertIn("dir=asc", result)
151+
152+
# asc -> desc
153+
result = render_sort_anchor(
154+
self._make_request({"sort": "name", "dir": "asc"}), "name", "Name", "asc"
155+
)
156+
self.assertIn("dir=desc", result)
157+
self.assertIn(settings.DEFAULT_SORT_UP, result)
83158

84-
def test_check_values(self):
85-
# Internal INVALID_FIELD_RAISES_404 is set at loading (no override_settings)
86-
saved = settings.INVALID_FIELD_RAISES_404
159+
# desc -> none (empty dir)
160+
result = render_sort_anchor(
161+
self._make_request({"sort": "name", "dir": "desc"}), "name", "Name", "asc"
162+
)
163+
self.assertIn(settings.DEFAULT_SORT_DOWN, result)
164+
self.assertNotIn("dir=asc", result)
165+
self.assertNotIn("dir=desc", result)
166+
167+
def test_desc_default_direction(self):
168+
"""With desc default, first click sorts descending."""
169+
result = render_sort_anchor(self._make_request(), "name", "Name", "desc")
170+
self.assertIn("dir=desc", result)
171+
172+
def test_css_class_support(self):
173+
"""CSS classes are added to anchor when configured."""
174+
saved_asc = settings.SORTING_CSS_CLASS_ASC
175+
saved_desc = settings.SORTING_CSS_CLASS_DESC
87176
try:
88-
settings.INVALID_FIELD_RAISES_404 = True
89-
response = self.client.get(
90-
self.nulls_last_url, {"sort": "filename", "nulls": "foo", "dir": "asc"}
177+
settings.SORTING_CSS_CLASS_ASC = "sorted-asc"
178+
settings.SORTING_CSS_CLASS_DESC = "sorted-desc"
179+
180+
result = render_sort_anchor(
181+
self._make_request({"sort": "name", "dir": "asc"}), "name", "Name", "asc"
91182
)
183+
self.assertIn('class="sorted-asc"', result)
184+
185+
result = render_sort_anchor(
186+
self._make_request({"sort": "name", "dir": "desc"}), "name", "Name", "asc"
187+
)
188+
self.assertIn('class="sorted-desc"', result)
92189
finally:
93-
settings.INVALID_FIELD_RAISES_404 = saved
190+
settings.SORTING_CSS_CLASS_ASC = saved_asc
191+
settings.SORTING_CSS_CLASS_DESC = saved_desc
192+
193+
194+
class TemplateTagParsingTest(TestCase):
195+
"""Test Django template tag parsing."""
196+
197+
def _render(self, template_str, context_vars=None):
198+
template = Template(template_str)
199+
request = HttpRequest()
200+
request.path = "/test/"
201+
request.GET = QueryDict()
202+
context = Context({"request": request, **(context_vars or {})})
203+
return template.render(context)
204+
205+
def test_anchor_with_quoted_title(self):
206+
result = self._render('{% load sorting_tags %}{% anchor field_name "Title" %}')
207+
self.assertIn("field_name", result)
208+
self.assertIn("Title", result)
209+
210+
def test_anchor_minimal(self):
211+
"""Field name is capitalized when no title given."""
212+
result = self._render("{% load sorting_tags %}{% anchor field_name %}")
213+
self.assertIn("Field_name", result)
214+
215+
def test_anchor_with_variable_title(self):
216+
result = self._render(
217+
"{% load sorting_tags %}{% anchor field_name my_title %}",
218+
{"my_title": "Dynamic Title"},
219+
)
220+
self.assertIn("Dynamic Title", result)
94221

95-
self.assertEqual(response.status_code, 404)
222+
def test_anchor_with_desc_default(self):
223+
result = self._render('{% load sorting_tags %}{% anchor field_name "Title" "desc" %}')
224+
self.assertIn("dir=desc", result)

0 commit comments

Comments
 (0)