Add toast notification system
Add more toast types
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
import django
|
||||
from django.conf import settings
|
||||
from django.test import TestCase, Client
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "timetracker.settings")
|
||||
django.setup()
|
||||
|
||||
from games.models import Device, Game, Platform, Purchase, Session
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
class MiddlewareIntegrationTest(TestCase):
|
||||
"""Integration tests for HTMXMessagesMiddleware.
|
||||
|
||||
These tests hit real endpoints that use messages.success() to verify
|
||||
the full chain: API endpoint → messages → middleware → HX-Trigger header.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def _create_user():
|
||||
return User.objects.create_user(
|
||||
username="testuser", password="testpass123"
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
self.user = self._create_user()
|
||||
self.client.force_login(self.user)
|
||||
pl = Platform(name="Test Platform")
|
||||
pl.save()
|
||||
self.game = Game(name="Test Game", platform=pl)
|
||||
self.game.save()
|
||||
|
||||
def test_non_htmx_request_with_message_gets_hx_trigger(self):
|
||||
"""
|
||||
Regression test: vanilla fetch() requests that set Django messages
|
||||
must receive HX-Trigger so fetchWithHtmxTriggers can read them.
|
||||
"""
|
||||
response = self.client.patch(
|
||||
f"/api/games/{self.game.id}/status",
|
||||
data=json.dumps({"status": "played"}),
|
||||
content_type="application/json",
|
||||
)
|
||||
self.assertEqual(response.status_code, 204)
|
||||
self.assertIn("HX-Trigger", response)
|
||||
data = json.loads(response["HX-Trigger"])
|
||||
self.assertIn("show-toast", data)
|
||||
self.assertEqual(data["show-toast"]["type"], "success")
|
||||
|
||||
def test_session_device_api_endpoint_sends_hx_trigger(self):
|
||||
"""
|
||||
Verify the session device API endpoint also produces HX-Trigger.
|
||||
This is the exact endpoint used by sessiondevice_selector.html.
|
||||
"""
|
||||
device = Device(name="Test Device")
|
||||
device.save()
|
||||
zt = ZoneInfo(settings.TIME_ZONE)
|
||||
session = Session(
|
||||
game=self.game,
|
||||
device=device,
|
||||
timestamp_start=datetime(2022, 9, 26, 14, 58, tzinfo=zt),
|
||||
)
|
||||
session.save()
|
||||
|
||||
response = self.client.patch(
|
||||
f"/api/session/{session.id}/device",
|
||||
data=json.dumps({"device_id": device.id}),
|
||||
content_type="application/json",
|
||||
)
|
||||
self.assertEqual(response.status_code, 204)
|
||||
self.assertIn("HX-Trigger", response)
|
||||
data = json.loads(response["HX-Trigger"])
|
||||
self.assertIn("show-toast", data)
|
||||
self.assertEqual(data["show-toast"]["message"], "Device updated")
|
||||
|
||||
def test_refund_purchase_returns_updated_row_with_hx_trigger(
|
||||
self,
|
||||
):
|
||||
"""
|
||||
Verify the refund endpoint returns the updated row HTML so the page
|
||||
swaps it in place without navigating away (preserving URL/query params).
|
||||
"""
|
||||
purchase = Purchase.objects.create(
|
||||
date_purchased=datetime(2023, 1, 1),
|
||||
platform=Platform.objects.first() or pl,
|
||||
)
|
||||
purchase.games.set([self.game])
|
||||
response = self.client.post(
|
||||
f"/tracker/purchase/{purchase.id}/refund",
|
||||
data={"set_abandoned": ""},
|
||||
)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertNotIn("HX-Redirect", response)
|
||||
self.assertIn("HX-Trigger", response)
|
||||
data = json.loads(response["HX-Trigger"])
|
||||
self.assertIn("show-toast", data)
|
||||
self.assertEqual(data["show-toast"]["message"], "Purchase refunded")
|
||||
# Verify the row HTML contains the updated row id
|
||||
body = response.content.decode()
|
||||
self.assertIn(f'purchase-row-{purchase.id}', body)
|
||||
# Verify OoO modal close element
|
||||
self.assertIn('hx-swap-oob', body)
|
||||
self.assertIn('refund-confirmation-modal', body)
|
||||
# Verify the purchase is actually refunded
|
||||
purchase.refresh_from_db()
|
||||
self.assertIsNotNone(purchase.date_refunded)
|
||||
Reference in New Issue
Block a user