feat(session): in-place row swap for finish/reset with OOB navbar

Delete stale _session_row_fragment; end_session and reset_session_start
return the canonical row plus an OOB navbar-playtime fragment. Clone keeps
HX-Refresh since it changes row count. Fixes #53.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-20 21:27:03 +02:00
parent 7d10884db7
commit 4a3e40ef29
3 changed files with 96 additions and 91 deletions
+6 -2
View File
@@ -267,7 +267,7 @@ class RenderedPagesTest(TestCase):
def test_reset_session_start_to_now_via_htmx(self):
# The inline "reset start" endpoint sets timestamp_start to now and
# asks htmx to refresh the list (the table is rebuilt server-side).
# returns an in-place row swap plus an OOB navbar update.
running = Session.objects.create(
game=self.game,
timestamp_start=datetime(2020, 1, 1, 10, 0, tzinfo=ZONEINFO),
@@ -277,7 +277,11 @@ class RenderedPagesTest(TestCase):
reverse("games:list_sessions_reset_session_start", args=[running.id]),
HTTP_HX_REQUEST="true",
)
self.assertEqual(resp["HX-Refresh"], "true")
self.assertEqual(resp.status_code, 200)
body = resp.content.decode()
self.assertIn(f'id="session-row-{running.id}"', body)
self.assertIn('id="navbar-playtime"', body)
self.assertNotIn("HX-Refresh", resp.headers)
running.refresh_from_db()
self.assertGreaterEqual(running.timestamp_start, before)
+70
View File
@@ -0,0 +1,70 @@
import pytest
from django.urls import reverse
from django.utils import timezone
from games.models import Device, Game, Platform, Session
@pytest.fixture
def auth_client(client, django_user_model):
user = django_user_model.objects.create_user(username="u", password="p")
client.force_login(user)
return client
@pytest.fixture
def running_session(db):
platform = Platform.objects.create(name="PC")
game = Game.objects.create(name="Hades", platform=platform)
device = Device.objects.create(name="Deck")
return Session.objects.create(
game=game, device=device, timestamp_start=timezone.now()
)
def test_end_session_htmx_returns_row_and_oob_navbar(auth_client, running_session):
url = reverse("games:list_sessions_end_session", args=[running_session.pk])
response = auth_client.get(url, HTTP_HX_REQUEST="true")
body = response.content.decode()
assert response.status_code == 200
assert f'id="session-row-{running_session.pk}"' in body
assert 'id="navbar-playtime"' in body
assert 'hx-swap-oob="true"' in body
running_session.refresh_from_db()
assert running_session.timestamp_end is not None
def test_reset_session_start_htmx_returns_row_no_refresh_header(
auth_client, running_session
):
original_start = running_session.timestamp_start
url = reverse(
"games:list_sessions_reset_session_start", args=[running_session.pk]
)
response = auth_client.get(url, HTTP_HX_REQUEST="true")
body = response.content.decode()
assert response.status_code == 200
assert f'id="session-row-{running_session.pk}"' in body
assert 'id="navbar-playtime"' in body
assert "HX-Refresh" not in response.headers
running_session.refresh_from_db()
assert running_session.timestamp_start > original_start
def test_clone_htmx_returns_hx_refresh(auth_client, running_session):
url = reverse(
"games:list_sessions_start_session_from_session",
args=[running_session.pk],
)
before = Session.objects.count()
response = auth_client.get(url, HTTP_HX_REQUEST="true")
assert response.status_code == 204
assert response.headers.get("HX-Refresh") == "true"
assert Session.objects.count() == before + 1
def test_end_session_non_htmx_redirects(auth_client, running_session):
url = reverse("games:list_sessions_end_session", args=[running_session.pk])
response = auth_client.get(url)
assert response.status_code == 302
assert response.url == reverse("games:list_sessions")