Clear pre-existing ruff lint + format debt (make check now green)

This commit is contained in:
2026-06-13 21:27:46 +02:00
parent 1258c529d2
commit c7af814364
11 changed files with 65 additions and 64 deletions
+1 -4
View File
@@ -17,7 +17,6 @@ widget into a ``DateCriterion`` unchanged. All behaviour is wired by
``games/static/js/date_range_picker.js``. ``games/static/js/date_range_picker.js``.
""" """
from common.components.core import Element, HTMLAttribute, Media, Node, Safe from common.components.core import Element, HTMLAttribute, Media, Node, Safe
from common.components.primitives import Div, Input, Span from common.components.primitives import Div, Input, Span
from common.time import DatePartSpec, date_parts from common.time import DatePartSpec, date_parts
@@ -101,9 +100,7 @@ def _iso_part_values(iso_value: str, parts: list[DatePartSpec]) -> dict[str, str
return values return values
def _segment_input( def _segment_input(*, part: DatePartSpec, side: str, label: str, value: str) -> Node:
*, part: DatePartSpec, side: str, label: str, value: str
) -> Node:
side_label = "from" if side == "min" else "to" side_label = "from" if side == "min" else "to"
return Input( return Input(
attributes=[ attributes=[
+1 -3
View File
@@ -173,9 +173,7 @@ def _split_modifier(modifier: str, has_m2m: bool = False) -> str:
return "" return ""
def _enum_filter( def _enum_filter(field_name: str, options, choice: FilterChoice, *, nullable) -> Node:
field_name: str, options, choice: FilterChoice, *, nullable
) -> Node:
"""A FilterSelect over a small, fully pre-rendered option set (enum field). """A FilterSelect over a small, fully pre-rendered option set (enum field).
Enum fields are single-valued, so no M2M modifiers (all/only are Enum fields are single-valued, so no M2M modifiers (all/only are
+1
View File
@@ -7,6 +7,7 @@ import pytest
# synchronous operations inside the async context safely. # synchronous operations inside the async context safely.
os.environ.setdefault("DJANGO_ALLOW_ASYNC_UNSAFE", "true") os.environ.setdefault("DJANGO_ALLOW_ASYNC_UNSAFE", "true")
@pytest.fixture(scope="session") @pytest.fixture(scope="session")
def browser_type_launch_args(browser_type_launch_args): def browser_type_launch_args(browser_type_launch_args):
# Try to find a system-installed Google Chrome or Chromium to bypass Nix/NixOS shared library issues # Try to find a system-installed Google Chrome or Chromium to bypass Nix/NixOS shared library issues
+1 -3
View File
@@ -121,9 +121,7 @@ def test_max_only_serializes_as_less_than(live_server, page):
".dispatchEvent(new Event('submit', {cancelable: true}))" ".dispatchEvent(new Event('submit', {cancelable: true}))"
) )
parsed = _filter_from_url(page.url) parsed = _filter_from_url(page.url)
assert parsed == { assert parsed == {"date_refunded": {"value": "2025-06-30", "modifier": "LESS_THAN"}}
"date_refunded": {"value": "2025-06-30", "modifier": "LESS_THAN"}
}
@pytest.mark.django_db @pytest.mark.django_db
+23 -25
View File
@@ -1,8 +1,4 @@
"""End-to-end Playwright test for the RangeSlider JS synchronization, cross-over, and clamping behavior. """End-to-end Playwright test for the RangeSlider JS synchronization, cross-over, and clamping behavior."""
"""
import json
import urllib.parse
import pytest import pytest
from django.http import HttpResponse from django.http import HttpResponse
@@ -41,17 +37,17 @@ urlpatterns = [
@override_settings(ROOT_URLCONF="e2e.test_range_slider_e2e") @override_settings(ROOT_URLCONF="e2e.test_range_slider_e2e")
def test_range_slider_crossover_min_higher_than_max(live_server, page): def test_range_slider_crossover_min_higher_than_max(live_server, page):
page.goto(live_server.url + "/test-range-slider/") page.goto(live_server.url + "/test-range-slider/")
# 1. Start with known state: Min is empty, Max is empty # 1. Start with known state: Min is empty, Max is empty
min_input = page.locator('input[name="filter-session-count-min"]') min_input = page.locator('input[name="filter-session-count-min"]')
max_input = page.locator('input[name="filter-session-count-max"]') max_input = page.locator('input[name="filter-session-count-max"]')
# 2. Type "20" into max input # 2. Type "20" into max input
max_input.fill("20") max_input.fill("20")
# 3. Type "50" into min input (which is higher than 20) # 3. Type "50" into min input (which is higher than 20)
min_input.fill("50") min_input.fill("50")
# 4. Max input should have automatically synchronized/snapped to 50 # 4. Max input should have automatically synchronized/snapped to 50
assert max_input.input_value() == "50" assert max_input.input_value() == "50"
@@ -60,16 +56,16 @@ def test_range_slider_crossover_min_higher_than_max(live_server, page):
@override_settings(ROOT_URLCONF="e2e.test_range_slider_e2e") @override_settings(ROOT_URLCONF="e2e.test_range_slider_e2e")
def test_range_slider_crossover_max_less_than_min(live_server, page): def test_range_slider_crossover_max_less_than_min(live_server, page):
page.goto(live_server.url + "/test-range-slider/") page.goto(live_server.url + "/test-range-slider/")
min_input = page.locator('input[name="filter-session-count-min"]') min_input = page.locator('input[name="filter-session-count-min"]')
max_input = page.locator('input[name="filter-session-count-max"]') max_input = page.locator('input[name="filter-session-count-max"]')
# 1. Type "50" into min input # 1. Type "50" into min input
min_input.fill("50") min_input.fill("50")
# 2. Type "30" into max input (which is less than 50) # 2. Type "30" into max input (which is less than 50)
max_input.fill("30") max_input.fill("30")
# 3. Min input should have automatically synchronized/snapped to 30 # 3. Min input should have automatically synchronized/snapped to 30
assert min_input.input_value() == "30" assert min_input.input_value() == "30"
@@ -78,20 +74,20 @@ def test_range_slider_crossover_max_less_than_min(live_server, page):
@override_settings(ROOT_URLCONF="e2e.test_range_slider_e2e") @override_settings(ROOT_URLCONF="e2e.test_range_slider_e2e")
def test_range_slider_strict_bounds_clamping_on_blur(live_server, page): def test_range_slider_strict_bounds_clamping_on_blur(live_server, page):
page.goto(live_server.url + "/test-range-slider/") page.goto(live_server.url + "/test-range-slider/")
min_input = page.locator('input[name="filter-session-count-min"]') min_input = page.locator('input[name="filter-session-count-min"]')
max_input = page.locator('input[name="filter-session-count-max"]') max_input = page.locator('input[name="filter-session-count-max"]')
# 1. Type value higher than dataMax (100 is max, type "150") # 1. Type value higher than dataMax (100 is max, type "150")
max_input.fill("150") max_input.fill("150")
max_input.blur() # triggers "change" event max_input.blur() # triggers "change" event
assert max_input.input_value() == "100" assert max_input.input_value() == "100"
# 2. Type value lower than dataMin (0 is min, type "-20") # 2. Type value lower than dataMin (0 is min, type "-20")
min_input.fill("-20") min_input.fill("-20")
min_input.blur() # triggers "change" event min_input.blur() # triggers "change" event
assert min_input.input_value() == "0" assert min_input.input_value() == "0"
@@ -99,18 +95,20 @@ def test_range_slider_strict_bounds_clamping_on_blur(live_server, page):
@override_settings(ROOT_URLCONF="e2e.test_range_slider_e2e") @override_settings(ROOT_URLCONF="e2e.test_range_slider_e2e")
def test_range_slider_empty_max_thumb_does_not_jump_to_beginning(live_server, page): def test_range_slider_empty_max_thumb_does_not_jump_to_beginning(live_server, page):
page.goto(live_server.url + "/test-range-slider/") page.goto(live_server.url + "/test-range-slider/")
# Locate handles # Locate handles
max_handle = page.locator('.range-handle-max[data-target="filter-session-count-max"]') max_handle = page.locator(
'.range-handle-max[data-target="filter-session-count-max"]'
)
# Initially, max_input is empty, so handle should sit at 100% (far right) # Initially, max_input is empty, so handle should sit at 100% (far right)
style = max_handle.get_attribute("style") style = max_handle.get_attribute("style")
assert "left:100%" in style or "left: 100%" in style assert "left:100%" in style or "left: 100%" in style
# Set min to 50 # Set min to 50
min_input = page.locator('input[name="filter-session-count-min"]') min_input = page.locator('input[name="filter-session-count-min"]')
min_input.fill("50") min_input.fill("50")
# Max handle should STILL stay at 100% since max input is still empty (defaults to max_value) # Max handle should STILL stay at 100% since max input is still empty (defaults to max_value)
style = max_handle.get_attribute("style") style = max_handle.get_attribute("style")
assert "left:100%" in style or "left: 100%" in style assert "left:100%" in style or "left: 100%" in style
+21 -17
View File
@@ -38,9 +38,7 @@ def prefilled_bar_view(request):
"value": "Switch", "value": "Switch",
"modifier": "INCLUDES", "modifier": "INCLUDES",
}, },
"group": { "group": {"modifier": "IS_NULL"},
"modifier": "IS_NULL"
}
} }
) )
return HttpResponse(_bar_page(filter_json=filter_json)) return HttpResponse(_bar_page(filter_json=filter_json))
@@ -63,19 +61,21 @@ def _filter_from_url(url: str) -> dict:
@override_settings(ROOT_URLCONF="e2e.test_string_filter_e2e") @override_settings(ROOT_URLCONF="e2e.test_string_filter_e2e")
def test_string_filter_defaults_and_toggles(live_server, page): def test_string_filter_defaults_and_toggles(live_server, page):
page.goto(live_server.url + "/test-string-filter-empty/") page.goto(live_server.url + "/test-string-filter-empty/")
# 1. Verify text inputs are active by default and modifier "is" (EQUALS) is checked # 1. Verify text inputs are active by default and modifier "is" (EQUALS) is checked
name_input = page.locator('input[name="filter-name"]') name_input = page.locator('input[name="filter-name"]')
assert name_input.is_enabled() assert name_input.is_enabled()
is_radio = page.locator('input[name="filter-name-modifier"][value="EQUALS"]') is_radio = page.locator('input[name="filter-name-modifier"][value="EQUALS"]')
assert is_radio.is_checked() assert is_radio.is_checked()
# 2. Enter values, click "includes" (INCLUDES), and submit # 2. Enter values, click "includes" (INCLUDES), and submit
name_input.fill("PlayStation") name_input.fill("PlayStation")
includes_radio = page.locator('input[name="filter-name-modifier"][value="INCLUDES"]') includes_radio = page.locator(
'input[name="filter-name-modifier"][value="INCLUDES"]'
)
includes_radio.click() includes_radio.click()
with page.expect_navigation(): with page.expect_navigation():
page.evaluate( page.evaluate(
"document.getElementById('filter-bar-form')" "document.getElementById('filter-bar-form')"
@@ -92,15 +92,15 @@ def test_string_filter_null_states(live_server, page):
name_input = page.locator('input[name="filter-name"]') name_input = page.locator('input[name="filter-name"]')
name_input.fill("Xbox") name_input.fill("Xbox")
# Click "is null" # Click "is null"
is_null_radio = page.locator('input[name="filter-name-modifier"][value="IS_NULL"]') is_null_radio = page.locator('input[name="filter-name-modifier"][value="IS_NULL"]')
is_null_radio.click() is_null_radio.click()
# Verification of interactive disabling # Verification of interactive disabling
assert not name_input.is_enabled() assert not name_input.is_enabled()
assert name_input.input_value() == "" assert name_input.input_value() == ""
with page.expect_navigation(): with page.expect_navigation():
page.evaluate( page.evaluate(
"document.getElementById('filter-bar-form')" "document.getElementById('filter-bar-form')"
@@ -114,19 +114,23 @@ def test_string_filter_null_states(live_server, page):
@override_settings(ROOT_URLCONF="e2e.test_string_filter_e2e") @override_settings(ROOT_URLCONF="e2e.test_string_filter_e2e")
def test_string_filter_prefilled_states(live_server, page): def test_string_filter_prefilled_states(live_server, page):
page.goto(live_server.url + "/test-string-filter-prefilled/") page.goto(live_server.url + "/test-string-filter-prefilled/")
name_input = page.locator('input[name="filter-name"]') name_input = page.locator('input[name="filter-name"]')
group_input = page.locator('input[name="filter-group"]') group_input = page.locator('input[name="filter-group"]')
# Verifies name matches "Switch" and "includes" is checked # Verifies name matches "Switch" and "includes" is checked
assert name_input.input_value() == "Switch" assert name_input.input_value() == "Switch"
assert name_input.is_enabled() assert name_input.is_enabled()
assert page.locator('input[name="filter-name-modifier"][value="INCLUDES"]').is_checked() assert page.locator(
'input[name="filter-name-modifier"][value="INCLUDES"]'
).is_checked()
# Verifies group is empty, disabled, and "is null" is checked # Verifies group is empty, disabled, and "is null" is checked
assert group_input.input_value() == "" assert group_input.input_value() == ""
assert not group_input.is_enabled() assert not group_input.is_enabled()
assert page.locator('input[name="filter-group-modifier"][value="IS_NULL"]').is_checked() assert page.locator(
'input[name="filter-group-modifier"][value="IS_NULL"]'
).is_checked()
@pytest.mark.django_db @pytest.mark.django_db
@@ -136,11 +140,11 @@ def test_string_filter_deselect_re_enables(live_server, page):
name_input = page.locator('input[name="filter-name"]') name_input = page.locator('input[name="filter-name"]')
is_null_radio = page.locator('input[name="filter-name-modifier"][value="IS_NULL"]') is_null_radio = page.locator('input[name="filter-name-modifier"][value="IS_NULL"]')
# 1. Click "is null" -> disables input # 1. Click "is null" -> disables input
is_null_radio.click() is_null_radio.click()
assert not name_input.is_enabled() assert not name_input.is_enabled()
# 2. Click "is null" again to deselect/uncheck -> should re-enable the text input # 2. Click "is null" again to deselect/uncheck -> should re-enable the text input
is_null_radio.click() is_null_radio.click()
assert name_input.is_enabled() assert name_input.is_enabled()
+3 -1
View File
@@ -412,7 +412,9 @@ class GameFilter(OperatorFilter):
from games.models import PlayEvent from games.models import PlayEvent
event_q = criterion.to_q("note") event_q = criterion.to_q("note")
matching_ids = PlayEvent.objects.filter(event_q).values_list("game_id", flat=True) matching_ids = PlayEvent.objects.filter(event_q).values_list(
"game_id", flat=True
)
return Q(id__in=matching_ids) return Q(id__in=matching_ids)
@@ -4,15 +4,14 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('games', '0017_add_filter_preset'), ("games", "0017_add_filter_preset"),
] ]
operations = [ operations = [
migrations.AlterField( migrations.AlterField(
model_name='session', model_name="session",
name='timestamp_start', name="timestamp_start",
field=models.DateTimeField(db_index=True, verbose_name='Start'), field=models.DateTimeField(db_index=True, verbose_name="Start"),
), ),
] ]
-1
View File
@@ -362,4 +362,3 @@ class FilterBarRenderingTest(TestCase):
self.assertIn('name="filter-refunded"', purchase_html) self.assertIn('name="filter-refunded"', purchase_html)
self.assertIn('value="true"', purchase_html) self.assertIn('value="true"', purchase_html)
self.assertIn('value="false"', purchase_html) self.assertIn('value="false"', purchase_html)
-1
View File
@@ -85,4 +85,3 @@ class ParseBoolNullableTest(SimpleTestCase):
self.assertTrue(_parse_bool_nullable({"field": {"value": "1"}}, "field")) self.assertTrue(_parse_bool_nullable({"field": {"value": "1"}}, "field"))
self.assertFalse(_parse_bool_nullable({"field": {"value": "false"}}, "field")) self.assertFalse(_parse_bool_nullable({"field": {"value": "false"}}, "field"))
self.assertFalse(_parse_bool_nullable({"field": {"value": "0"}}, "field")) self.assertFalse(_parse_bool_nullable({"field": {"value": "0"}}, "field"))
+10 -4
View File
@@ -560,8 +560,14 @@ class TestFilterBarRendering:
def test_mastered_not_checked_by_default(self): def test_mastered_not_checked_by_default(self):
html = str(FilterBar(filter_json="")) html = str(FilterBar(filter_json=""))
assert 'name="filter-mastered" value="true" class="rounded-full border-default-medium bg-neutral-secondary-medium text-brand focus:ring-brand" checked="true"' not in html assert (
assert 'name="filter-mastered" value="false" class="rounded-full border-default-medium bg-neutral-secondary-medium text-brand focus:ring-brand" checked="true"' not in html 'name="filter-mastered" value="true" class="rounded-full border-default-medium bg-neutral-secondary-medium text-brand focus:ring-brand" checked="true"'
not in html
)
assert (
'name="filter-mastered" value="false" class="rounded-full border-default-medium bg-neutral-secondary-medium text-brand focus:ring-brand" checked="true"'
not in html
)
def test_mastered_checked_when_filtered(self): def test_mastered_checked_when_filtered(self):
html = str( html = str(
@@ -784,7 +790,7 @@ class TestExpandedFiltersAgainstDB:
from games.filters import SessionFilter from games.filters import SessionFilter
from games.models import Session from games.models import Session
data = self._setup_entities() self._setup_entities()
# Test duration_total_hours equals 4 # Test duration_total_hours equals 4
sf_tot = SessionFilter.from_json( sf_tot = SessionFilter.from_json(
@@ -808,7 +814,7 @@ class TestExpandedFiltersAgainstDB:
from games.filters import PurchaseFilter from games.filters import PurchaseFilter
from games.models import Purchase from games.models import Purchase
data = self._setup_entities() self._setup_entities()
pf = PurchaseFilter.from_json( pf = PurchaseFilter.from_json(
{ {