in role-select.js, selectRole() runs in more precise ordering to ensure card hand for role selection passes to the next gamer after a selection is made; previous bug allowed multiple cards at a single gamer position, which prevented the card hand from making a circuit around the table before depletion; backend fixes including to apps.epic.views.select_role; +2 FTs & +1 IT asserts these features
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
Disco DeDisco
2026-03-21 14:33:06 -04:00
parent 91e0eaad8e
commit f5c2cf4636
4 changed files with 120 additions and 10 deletions

View File

@@ -403,6 +403,94 @@ class RoleSelectTest(FunctionalTest):
)
# ------------------------------------------------------------------ #
# Test 4b — Stack locks out immediately after selection (no WS) #
# ------------------------------------------------------------------ #
def test_card_stack_ineligible_immediately_after_selection(self):
"""After clicking a role card the stack must flip to
data-state='ineligible' straight away — before any WS turn_changed
event could arrive. This test runs without a Channels server so
no WS event will fire; the fix must be entirely client-side."""
founder, _ = User.objects.get_or_create(email="founder@test.io")
room = Room.objects.create(name="Lockout Test", owner=founder)
_fill_room_via_orm(room, [
"founder@test.io", "amigo@test.io", "bud@test.io",
"pal@test.io", "dude@test.io", "bro@test.io",
])
room.table_status = Room.ROLE_SELECT
room.save()
for slot in room.gate_slots.order_by("slot_number"):
TableSeat.objects.create(
room=room, gamer=slot.gamer, slot_number=slot.slot_number,
)
room_url = f"{self.live_server_url}/gameboard/room/{room.id}/gate/"
self.create_pre_authenticated_session("founder@test.io")
self.browser.get(room_url)
self.wait_for(
lambda: self.browser.find_element(
By.CSS_SELECTOR, ".card-stack[data-state='eligible']"
)
).click()
self.wait_for(lambda: self.browser.find_element(By.ID, "id_role_select"))
self.browser.find_element(By.CSS_SELECTOR, "#id_role_select .card").click()
# No WS — only the JS fix can make this transition happen
self.wait_for(
lambda: self.assertEqual(
self.browser.find_element(
By.CSS_SELECTOR, ".card-stack"
).get_attribute("data-state"),
"ineligible",
)
)
def test_card_stack_cannot_be_reopened_after_selection(self):
"""Clicking the card stack immediately after picking a role must
not open a second fan — the listener must have been removed."""
founder, _ = User.objects.get_or_create(email="founder@test.io")
room = Room.objects.create(name="No-reopen Test", owner=founder)
_fill_room_via_orm(room, [
"founder@test.io", "amigo@test.io", "bud@test.io",
"pal@test.io", "dude@test.io", "bro@test.io",
])
room.table_status = Room.ROLE_SELECT
room.save()
for slot in room.gate_slots.order_by("slot_number"):
TableSeat.objects.create(
room=room, gamer=slot.gamer, slot_number=slot.slot_number,
)
room_url = f"{self.live_server_url}/gameboard/room/{room.id}/gate/"
self.create_pre_authenticated_session("founder@test.io")
self.browser.get(room_url)
# Open fan, pick a card
self.wait_for(
lambda: self.browser.find_element(
By.CSS_SELECTOR, ".card-stack[data-state='eligible']"
)
).click()
self.wait_for(lambda: self.browser.find_element(By.ID, "id_role_select"))
self.browser.find_element(By.CSS_SELECTOR, "#id_role_select .card").click()
# Wait for fan to close (selectRole closes it synchronously)
self.wait_for(
lambda: self.assertEqual(
len(self.browser.find_elements(By.ID, "id_role_select")), 0
)
)
# Attempt to reopen — must not work
self.browser.find_element(By.CSS_SELECTOR, ".card-stack").click()
self.wait_for(
lambda: self.assertEqual(
len(self.browser.find_elements(By.ID, "id_role_select")), 0
)
)
# ------------------------------------------------------------------ #
# Test 7 — All roles revealed simultaneously after all gamers select #
# ------------------------------------------------------------------ #