Refine filters
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
"""Characterization tests locking the rendered output of the three filter bars.
|
||||
|
||||
The FilterBar family (FilterBar / SessionFilterBar / PurchaseFilterBar) is the
|
||||
target of an upcoming dedup + module split. These tests pin the structural
|
||||
contract — form/input ids, the hidden ``filter`` field, preset wiring, the
|
||||
filter_json round-trip, and no double-escaping — so that refactor stays
|
||||
behaviour-preserving. The renderers were previously untested.
|
||||
target of a dedup + module split + RangeSlider component extraction. These tests
|
||||
pin the structural contract — form/input ids, the hidden ``filter`` field,
|
||||
preset wiring, the filter_json round-trip, no double-escaping, and the
|
||||
Flowbite-styled native range slider unification — so that refactor stays
|
||||
behaviour-preserving.
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -41,6 +42,24 @@ class FilterBarRenderingTest(TestCase):
|
||||
self.assertIn(save_url, html) # preset save URL wired in
|
||||
self.assertNoEscapedTags(html)
|
||||
|
||||
def _assert_range_slider(self, html):
|
||||
"""Every filter bar must use the RangeSlider component with custom
|
||||
draggable <div> handles, a track fill, and mode-toggle button."""
|
||||
self.assertIn("range-slider-block", html)
|
||||
self.assertIn('data-mode="range"', html)
|
||||
self.assertIn("range-mode-toggle", html)
|
||||
self.assertIn("range-mode-icon-range", html)
|
||||
self.assertIn("range-mode-icon-point", html)
|
||||
self.assertIn("range-track-fill", html)
|
||||
self.assertIn("range-handle-min", html)
|
||||
self.assertIn("range-handle-max", html)
|
||||
# No native range inputs
|
||||
self.assertNotIn(
|
||||
'<input type="range"',
|
||||
html,
|
||||
"native <input type=range> found — should use custom div handles",
|
||||
)
|
||||
|
||||
def test_game_filter_bar(self):
|
||||
html = str(
|
||||
FilterBar(
|
||||
@@ -50,6 +69,7 @@ class FilterBarRenderingTest(TestCase):
|
||||
)
|
||||
)
|
||||
self._assert_shell(html, "/presets/games/list", "/presets/games/save")
|
||||
self._assert_range_slider(html)
|
||||
|
||||
def test_session_filter_bar(self):
|
||||
html = str(
|
||||
@@ -60,6 +80,7 @@ class FilterBarRenderingTest(TestCase):
|
||||
)
|
||||
)
|
||||
self._assert_shell(html, "/presets/sessions/list", "/presets/sessions/save")
|
||||
self._assert_range_slider(html)
|
||||
|
||||
def test_purchase_filter_bar(self):
|
||||
html = str(
|
||||
@@ -70,6 +91,7 @@ class FilterBarRenderingTest(TestCase):
|
||||
)
|
||||
)
|
||||
self._assert_shell(html, "/presets/purchases/list", "/presets/purchases/save")
|
||||
self._assert_range_slider(html)
|
||||
|
||||
def test_game_filter_bar_roundtrips_selected_status(self):
|
||||
"""A status in filter_json renders as a selected tag in the widget."""
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
"""Unit tests for filter JSON parsing helpers."""
|
||||
|
||||
from django.test import SimpleTestCase
|
||||
|
||||
from common.components.filters import _parse_bool, _parse_range
|
||||
|
||||
|
||||
class ParseRangeTest(SimpleTestCase):
|
||||
def test_empty_dict(self):
|
||||
self.assertEqual(_parse_range({}, "field"), ("", ""))
|
||||
|
||||
def test_missing_key(self):
|
||||
self.assertEqual(_parse_range({"other": 1}, "field"), ("", ""))
|
||||
|
||||
def test_null_value(self):
|
||||
self.assertEqual(_parse_range({"field": None}, "field"), ("", ""))
|
||||
|
||||
def test_non_dict_value(self):
|
||||
"""A non-dict field value is coerced to ("", "")."""
|
||||
self.assertEqual(_parse_range({"field": "not_a_dict"}, "field"), ("", ""))
|
||||
|
||||
def test_value_only(self):
|
||||
self.assertEqual(_parse_range({"field": {"value": "10"}}, "field"), ("10", ""))
|
||||
|
||||
def test_value_and_value2(self):
|
||||
self.assertEqual(
|
||||
_parse_range({"field": {"value": "10", "value2": "20"}}, "field"),
|
||||
("10", "20"),
|
||||
)
|
||||
|
||||
def test_empty_strings(self):
|
||||
self.assertEqual(
|
||||
_parse_range({"field": {"value": "", "value2": ""}}, "field"), ("", "")
|
||||
)
|
||||
|
||||
def test_integer_values_become_strings(self):
|
||||
self.assertEqual(
|
||||
_parse_range({"field": {"value": 5, "value2": 15}}, "field"),
|
||||
("5", "15"),
|
||||
)
|
||||
|
||||
|
||||
class ParseBoolTest(SimpleTestCase):
|
||||
def test_empty_dict(self):
|
||||
self.assertFalse(_parse_bool({}, "field"))
|
||||
|
||||
def test_missing_key(self):
|
||||
self.assertFalse(_parse_bool({"other": 1}, "field"))
|
||||
|
||||
def test_null_value(self):
|
||||
self.assertFalse(_parse_bool({"field": None}, "field"))
|
||||
|
||||
def test_non_dict_value(self):
|
||||
"""A non-dict field value is coerced to False."""
|
||||
self.assertFalse(_parse_bool({"field": "not_a_dict"}, "field"))
|
||||
|
||||
def test_false_value(self):
|
||||
self.assertFalse(_parse_bool({"field": {"value": False}}, "field"))
|
||||
|
||||
def test_true_value(self):
|
||||
self.assertTrue(_parse_bool({"field": {"value": True}}, "field"))
|
||||
|
||||
def test_truthy_string(self):
|
||||
"""Non-empty strings are truthy — bool("yes") is True."""
|
||||
self.assertTrue(_parse_bool({"field": {"value": "yes"}}, "field"))
|
||||
|
||||
def test_missing_value_in_field(self):
|
||||
self.assertFalse(_parse_bool({"field": {}}, "field"))
|
||||
Reference in New Issue
Block a user