pick_sigs view + cursor polarity groups; game kit gear menu; housekeeping
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
roles_revealed WS event removed; select_role last pick now fires _notify_all_roles_filled() + stays in ROLE_SELECT; new pick_sigs view (POST /room/<uuid>/pick-sigs) transitions ROLE_SELECT→SIG_SELECT + broadcasts sig_select_started; room.html shows .pick-sigs-btn when all 6 roles filled; PICK SIGS btn absent during mid-selection; 11 new/modified ITs in SelectRoleViewTest + RoomViewAllRolesFilledTest + PickSigsViewTest
consumer: LEVITY_ROLES {PC/NC/SC} + GRAVITY_ROLES {BC/EC/AC}; connects to per-polarity cursor group (cursors_{id}_levity/gravity); receive_json routes cursor_move to cursor group; new handlers all_roles_filled, sig_select_started, cursor_move; CursorMoveConsumerTest (TransactionTestCase, @tag channels): levity cursor reaches fellow levity player, does not reach gravity player
game kit gear menu: #id_game_kit_menu registered in _applets.scss %applet-menu + fixed-position + landscape offset; id_gk_sections_container added to appletContainerIds in applets.js so OK submit dismisses menu; _game_kit_sections.html sections use entry.applet.grid_cols/grid_rows (was hardcoded 6); %applets-grid applied to #id_gk_sections_container (direct parent of sections, not outer wrapper); FT setUp seeds gk-* applets via get_or_create
drama test reorg: integrated/test_views.py deleted (no drama views); two test classes moved to epic/tests/integrated/test_views.py + GameEvent import added; drama/tests/unit/test_models.py → drama/tests/integrated/test_models.py; unit/ dir removed
login form: position:fixed + vertically centred in base styles across all breakpoints; 24rem width, text-align:center; landscape block reduced to left/right sidebar offsets; alert moved below h2; left-side position indicator slots 3/4/5 column order flipped via CSS data-slot selectors
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
|
||||
from apps.drama.models import GameEvent
|
||||
from apps.lyric.models import Token, User
|
||||
from apps.epic.models import (
|
||||
DeckVariant, GateSlot, Room, RoomInvite, TableSeat, TarotCard,
|
||||
@@ -643,7 +644,7 @@ class SelectRoleViewTest(TestCase):
|
||||
).order_by("slot_number").first()
|
||||
self.assertEqual(next_active.slot_number, 2)
|
||||
|
||||
def test_all_selected_sets_sig_select(self):
|
||||
def test_all_selected_stays_role_select_status(self):
|
||||
roles = ["PC", "BC", "SC", "AC", "NC"]
|
||||
for i, role in enumerate(roles):
|
||||
seat = TableSeat.objects.get(room=self.room, slot_number=i + 1)
|
||||
@@ -655,7 +656,7 @@ class SelectRoleViewTest(TestCase):
|
||||
data={"role": "EC"},
|
||||
)
|
||||
self.room.refresh_from_db()
|
||||
self.assertEqual(self.room.table_status, Room.SIG_SELECT)
|
||||
self.assertEqual(self.room.table_status, Room.ROLE_SELECT)
|
||||
|
||||
def test_select_role_notifies_turn_changed(self):
|
||||
with patch("apps.epic.views._notify_turn_changed") as mock_notify:
|
||||
@@ -665,14 +666,14 @@ class SelectRoleViewTest(TestCase):
|
||||
)
|
||||
mock_notify.assert_called_once_with(self.room.id)
|
||||
|
||||
def test_select_role_notifies_roles_revealed_when_last(self):
|
||||
def test_select_role_notifies_all_roles_filled_when_last(self):
|
||||
roles = ["PC", "BC", "SC", "AC", "NC"]
|
||||
for i, role in enumerate(roles):
|
||||
seat = TableSeat.objects.get(room=self.room, slot_number=i + 1)
|
||||
seat.role = role
|
||||
seat.save()
|
||||
self.client.force_login(self.gamers[5])
|
||||
with patch("apps.epic.views._notify_roles_revealed") as mock_notify:
|
||||
with patch("apps.epic.views._notify_all_roles_filled") as mock_notify:
|
||||
self.client.post(
|
||||
reverse("epic:select_role", kwargs={"room_id": self.room.id}),
|
||||
data={"role": "EC"},
|
||||
@@ -742,6 +743,75 @@ class SelectRoleViewTest(TestCase):
|
||||
)
|
||||
|
||||
|
||||
class RoomViewAllRolesFilledTest(TestCase):
|
||||
"""Room view in ROLE_SELECT with all seats assigned shows PICK SIGS button."""
|
||||
def setUp(self):
|
||||
import lxml.html
|
||||
self.lxml = lxml.html
|
||||
self.owner = User.objects.create(email="owner@test.io")
|
||||
self.room = Room.objects.create(name="Test Room", owner=self.owner)
|
||||
self.room.table_status = Room.ROLE_SELECT
|
||||
self.room.save()
|
||||
all_roles = ["PC", "BC", "SC", "AC", "NC", "EC"]
|
||||
for i, role in enumerate(all_roles, start=1):
|
||||
user = User.objects.create(email=f"p{i}@test.io")
|
||||
TableSeat.objects.create(room=self.room, gamer=user, slot_number=i, role=role)
|
||||
self.client.force_login(self.owner)
|
||||
|
||||
def test_pick_sigs_btn_present_when_all_roles_filled(self):
|
||||
response = self.client.get(reverse("epic:room", kwargs={"room_id": self.room.id}))
|
||||
parsed = self.lxml.fromstring(response.content)
|
||||
[_] = parsed.cssselect(".pick-sigs-btn")
|
||||
|
||||
def test_pick_sigs_btn_absent_during_role_select(self):
|
||||
# Clear one role — still mid-pick, button must not appear
|
||||
TableSeat.objects.filter(room=self.room, slot_number=6).update(role=None)
|
||||
response = self.client.get(reverse("epic:room", kwargs={"room_id": self.room.id}))
|
||||
parsed = self.lxml.fromstring(response.content)
|
||||
self.assertEqual(len(parsed.cssselect(".pick-sigs-btn")), 0)
|
||||
|
||||
|
||||
class PickSigsViewTest(TestCase):
|
||||
def setUp(self):
|
||||
self.owner = User.objects.create(email="owner@test.io")
|
||||
self.room = Room.objects.create(name="Test Room", owner=self.owner)
|
||||
self.room.table_status = Room.ROLE_SELECT
|
||||
self.room.save()
|
||||
all_roles = ["PC", "BC", "SC", "AC", "NC", "EC"]
|
||||
for i, role in enumerate(all_roles, start=1):
|
||||
user = User.objects.create(email=f"p{i}@test.io")
|
||||
TableSeat.objects.create(room=self.room, gamer=user, slot_number=i, role=role)
|
||||
self.client.force_login(self.owner)
|
||||
self.url = reverse("epic:pick_sigs", kwargs={"room_id": self.room.id})
|
||||
|
||||
def test_pick_sigs_requires_login(self):
|
||||
self.client.logout()
|
||||
response = self.client.post(self.url)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertIn("/accounts/login/", response.url)
|
||||
|
||||
def test_pick_sigs_transitions_to_sig_select(self):
|
||||
self.client.post(self.url)
|
||||
self.room.refresh_from_db()
|
||||
self.assertEqual(self.room.table_status, Room.SIG_SELECT)
|
||||
|
||||
def test_pick_sigs_redirects_to_room(self):
|
||||
response = self.client.post(self.url)
|
||||
self.assertRedirects(response, reverse("epic:room", args=[self.room.id]))
|
||||
|
||||
def test_pick_sigs_is_noop_if_not_role_select(self):
|
||||
self.room.table_status = Room.SIG_SELECT
|
||||
self.room.save()
|
||||
self.client.post(self.url)
|
||||
self.room.refresh_from_db()
|
||||
self.assertEqual(self.room.table_status, Room.SIG_SELECT)
|
||||
|
||||
def test_pick_sigs_notifies_sig_select_started(self):
|
||||
with patch("apps.epic.views._notify_sig_select_started") as mock_notify:
|
||||
self.client.post(self.url)
|
||||
mock_notify.assert_called_once_with(self.room.id)
|
||||
|
||||
|
||||
class RoomActionsViewTest(TestCase):
|
||||
def setUp(self):
|
||||
self.owner = User.objects.create(email="owner@test.io")
|
||||
@@ -987,3 +1057,63 @@ class SelectSigCardViewTest(TestCase):
|
||||
).first()
|
||||
response = self.client.post(self.url, data={"card_id": last_card.id})
|
||||
self.assertIn(response.status_code, (200, 302))
|
||||
|
||||
|
||||
class ConfirmTokenRecordsSlotFilledTest(TestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create(email="gamer@test.io")
|
||||
self.client.force_login(self.user)
|
||||
self.room = Room.objects.create(name="Test Room", owner=self.user)
|
||||
self.token = Token.objects.create(user=self.user, token_type=Token.TITHE)
|
||||
self.slot = self.room.gate_slots.get(slot_number=1)
|
||||
self.slot.gamer = self.user
|
||||
self.slot.status = GateSlot.RESERVED
|
||||
self.slot.reserved_at = timezone.now()
|
||||
self.slot.save()
|
||||
|
||||
def test_confirm_token_records_slot_filled_event(self):
|
||||
session = self.client.session
|
||||
session["kit_token_id"] = str(self.token.id)
|
||||
session.save()
|
||||
self.client.post(reverse("epic:confirm_token", args=[self.room.id]))
|
||||
event = GameEvent.objects.get(room=self.room, verb=GameEvent.SLOT_FILLED)
|
||||
self.assertEqual(event.actor, self.user)
|
||||
self.assertEqual(event.data["slot_number"], 1)
|
||||
self.assertEqual(event.data["token_type"], Token.TITHE)
|
||||
|
||||
def test_no_event_recorded_if_no_reserved_slot(self):
|
||||
self.slot.gamer = None
|
||||
self.slot.status = GateSlot.EMPTY
|
||||
self.slot.save()
|
||||
self.client.post(reverse("epic:confirm_token", args=[self.room.id]))
|
||||
self.assertEqual(GameEvent.objects.filter(verb=GameEvent.SLOT_FILLED).count(), 0)
|
||||
|
||||
|
||||
class SelectRoleRecordsRoleSelectedTest(TestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create(email="player@test.io")
|
||||
self.client.force_login(self.user)
|
||||
self.room = Room.objects.create(
|
||||
name="Role Room", owner=self.user, table_status=Room.ROLE_SELECT
|
||||
)
|
||||
self.seat = TableSeat.objects.create(
|
||||
room=self.room, gamer=self.user, slot_number=1
|
||||
)
|
||||
|
||||
def test_select_role_records_role_selected_event(self):
|
||||
self.client.post(
|
||||
reverse("epic:select_role", args=[self.room.id]),
|
||||
data={"role": "PC"},
|
||||
)
|
||||
event = GameEvent.objects.get(room=self.room, verb=GameEvent.ROLE_SELECTED)
|
||||
self.assertEqual(event.actor, self.user)
|
||||
self.assertEqual(event.data["role"], "PC")
|
||||
self.assertEqual(event.data["slot_number"], 1)
|
||||
|
||||
def test_no_event_if_role_already_taken(self):
|
||||
TableSeat.objects.create(room=self.room, gamer=self.user, slot_number=2, role="PC")
|
||||
self.client.post(
|
||||
reverse("epic:select_role", args=[self.room.id]),
|
||||
data={"role": "PC"},
|
||||
)
|
||||
self.assertEqual(GameEvent.objects.filter(verb=GameEvent.ROLE_SELECTED).count(), 0)
|
||||
|
||||
Reference in New Issue
Block a user