2026-04-27 22:22:08 -04:00
|
|
|
"""
|
|
|
|
|
Functional tests: deck-to-seat contribution mechanics.
|
|
|
|
|
|
|
|
|
|
Sprint 1 (DeckContributionTest):
|
|
|
|
|
Confirming a role in ROLE SELECT assigns the gamer's equipped deck to the
|
|
|
|
|
TableSeat. The Game Kit immediately reflects the in-use state: the deck
|
|
|
|
|
card gains the game name in its tooltip and the micro-status reads "In-Use".
|
|
|
|
|
|
|
|
|
|
Sprint 2 (DeckInUseGameKitTest):
|
|
|
|
|
With a deck already assigned to an active seat the Game Kit DON button is
|
|
|
|
|
btn-disabled and no DOFF toggle is shown (unlike the normal equipped state
|
|
|
|
|
where DOFF is visible). Clicking the card shows a tooltip naming the game.
|
|
|
|
|
A second deck that is NOT assigned to any active seat has normal DON/DOFF
|
|
|
|
|
behaviour.
|
|
|
|
|
"""
|
|
|
|
|
import time
|
|
|
|
|
|
|
|
|
|
from django.test import tag
|
|
|
|
|
from selenium.webdriver.common.by import By
|
|
|
|
|
|
|
|
|
|
from apps.applets.models import Applet
|
|
|
|
|
from apps.epic.models import DeckVariant, GateSlot, Room, TableSeat
|
|
|
|
|
from apps.lyric.models import User
|
|
|
|
|
from functional_tests.base import FunctionalTest
|
|
|
|
|
from functional_tests.test_room_role_select import _fill_room_via_orm
|
|
|
|
|
from functional_tests.test_room_sig_select import _assign_all_roles
|
|
|
|
|
|
|
|
|
|
FOUNDER_EMAIL = "founder@test.io"
|
|
|
|
|
GAMER_EMAIL = "gamer@test.io"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _equip_earthman(user):
|
|
|
|
|
earthman = DeckVariant.objects.get(slug="earthman")
|
|
|
|
|
user.equipped_deck = earthman
|
|
|
|
|
user.save(update_fields=["equipped_deck"])
|
|
|
|
|
return earthman
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _room_at_role_select(name="Wildfire"):
|
|
|
|
|
"""Create a room filled to 6 and at ROLE_SELECT table status."""
|
|
|
|
|
founder, _ = User.objects.get_or_create(email=FOUNDER_EMAIL)
|
|
|
|
|
room = Room.objects.create(name=name, owner=founder)
|
|
|
|
|
emails = [FOUNDER_EMAIL, GAMER_EMAIL,
|
|
|
|
|
"b@test.io", "c@test.io", "d@test.io", "e@test.io"]
|
|
|
|
|
_fill_room_via_orm(room, emails)
|
|
|
|
|
for slot in room.gate_slots.all():
|
|
|
|
|
if slot.gamer:
|
|
|
|
|
_equip_earthman(slot.gamer)
|
|
|
|
|
room.table_status = Room.ROLE_SELECT
|
|
|
|
|
room.save()
|
|
|
|
|
return room, founder
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ── Sprint 1 ─────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
class DeckContributionTest(FunctionalTest):
|
|
|
|
|
"""Sprint 1: role confirmation → TableSeat.deck_variant set; Game Kit shows In-Use."""
|
|
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
super().setUp()
|
|
|
|
|
self.browser.set_window_size(800, 1200)
|
|
|
|
|
for slug, name, ctx in [
|
|
|
|
|
("game-kit", "Game Kit", "gameboard"),
|
|
|
|
|
("gk-decks", "Card Decks","game-kit"),
|
|
|
|
|
]:
|
|
|
|
|
Applet.objects.get_or_create(slug=slug, defaults={"name": name, "context": ctx})
|
|
|
|
|
|
|
|
|
|
def test_confirming_role_assigns_equipped_deck_to_seat(self):
|
|
|
|
|
"""After the gamer confirms a role, Game Kit shows the Earthman deck as 'In-Use'
|
|
|
|
|
with the game name in the deck tooltip, and the micro-status changes from
|
|
|
|
|
'Equipped' to 'In-Use'.
|
|
|
|
|
"""
|
|
|
|
|
room, founder = _room_at_role_select()
|
|
|
|
|
gamer = User.objects.get(email=GAMER_EMAIL)
|
|
|
|
|
|
|
|
|
|
# Gamer logs in and navigates to the role-select room
|
|
|
|
|
session_key = self.create_pre_authenticated_session(GAMER_EMAIL)
|
|
|
|
|
self.browser.get(self.live_server_url)
|
|
|
|
|
self.browser.add_cookie({"name": "sessionid", "value": session_key})
|
|
|
|
|
self.browser.get(self.live_server_url + f"/gameboard/room/{room.pk}/")
|
|
|
|
|
|
|
|
|
|
# Gamer confirms a role (NC — slot 2 is theirs)
|
|
|
|
|
role_btn = self.wait_for(
|
|
|
|
|
lambda: self.browser.find_element(By.CSS_SELECTOR, ".role-card[data-role='NC'] .btn-confirm")
|
|
|
|
|
)
|
|
|
|
|
role_btn.click()
|
|
|
|
|
|
|
|
|
|
# Deck is now assigned to the seat in the DB
|
|
|
|
|
self.wait_for(lambda: self.assertTrue(
|
|
|
|
|
TableSeat.objects.filter(
|
|
|
|
|
gamer=gamer,
|
|
|
|
|
room=room,
|
|
|
|
|
deck_variant=gamer.equipped_deck,
|
|
|
|
|
).exists(),
|
|
|
|
|
"TableSeat.deck_variant was not set after role confirmation",
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
# Navigate to Game Kit → Card Decks to verify UI state
|
2026-04-27 23:24:43 -04:00
|
|
|
self.browser.get(self.live_server_url + "/gameboard/")
|
2026-04-27 22:22:08 -04:00
|
|
|
decks_btn = self.wait_for(
|
|
|
|
|
lambda: self.browser.find_element(By.CSS_SELECTOR, "#id_kit_card_deck")
|
|
|
|
|
)
|
|
|
|
|
decks_btn.click()
|
|
|
|
|
|
|
|
|
|
earthman_card = self.wait_for(
|
|
|
|
|
lambda: self.browser.find_element(By.CSS_SELECTOR, "#id_kit_earthman_deck")
|
|
|
|
|
)
|
|
|
|
|
earthman_card.click() # open tooltip
|
|
|
|
|
|
|
|
|
|
# Tooltip shows the game name
|
|
|
|
|
tooltip = self.wait_for(
|
|
|
|
|
lambda: self.browser.find_element(By.CSS_SELECTOR, ".tt-deck-game-name")
|
|
|
|
|
)
|
|
|
|
|
self.assertIn(room.name.upper(), tooltip.text.upper())
|
|
|
|
|
|
|
|
|
|
# Micro-status reads "In-Use", not "Equipped"
|
|
|
|
|
micro_status = self.wait_for(
|
|
|
|
|
lambda: self.browser.find_element(
|
|
|
|
|
By.CSS_SELECTOR, "#id_kit_earthman_deck .deck-micro-status"
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
self.assertIn("IN-USE", micro_status.text.upper())
|
|
|
|
|
self.assertNotIn("EQUIPPED", micro_status.text.upper())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ── Sprint 2 ─────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
class DeckInUseGameKitTest(FunctionalTest):
|
|
|
|
|
"""Sprint 2: DON disabled + no DOFF toggle for in-use deck; second deck unaffected."""
|
|
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
super().setUp()
|
|
|
|
|
self.browser.set_window_size(800, 1200)
|
|
|
|
|
for slug, name, ctx in [
|
|
|
|
|
("game-kit", "Game Kit", "gameboard"),
|
|
|
|
|
("gk-decks", "Card Decks","game-kit"),
|
|
|
|
|
]:
|
|
|
|
|
Applet.objects.get_or_create(slug=slug, defaults={"name": name, "context": ctx})
|
|
|
|
|
|
|
|
|
|
def _setup_in_use_deck(self):
|
|
|
|
|
"""Create a seated gamer whose Earthman deck is already assigned to a seat."""
|
|
|
|
|
room, founder = _room_at_role_select()
|
|
|
|
|
gamer = User.objects.get(email=GAMER_EMAIL)
|
|
|
|
|
earthman = _equip_earthman(gamer)
|
|
|
|
|
# Assign deck directly (Sprint 1 must be green first)
|
|
|
|
|
seat = TableSeat.objects.create(gamer=gamer, room=room, role="NC", deck_variant=earthman)
|
|
|
|
|
return gamer, earthman, room, seat
|
|
|
|
|
|
|
|
|
|
def test_don_is_disabled_and_doff_absent_for_in_use_deck(self):
|
|
|
|
|
"""DON button carries btn-disabled; DOFF is not rendered at all (not just disabled)."""
|
|
|
|
|
gamer, earthman, room, seat = self._setup_in_use_deck()
|
|
|
|
|
session_key = self.create_pre_authenticated_session(GAMER_EMAIL)
|
|
|
|
|
self.browser.get(self.live_server_url)
|
|
|
|
|
self.browser.add_cookie({"name": "sessionid", "value": session_key})
|
2026-04-27 23:24:43 -04:00
|
|
|
self.browser.get(self.live_server_url + "/gameboard/")
|
2026-04-27 22:22:08 -04:00
|
|
|
self.wait_for(
|
|
|
|
|
lambda: self.browser.find_element(By.CSS_SELECTOR, "#id_kit_card_deck")
|
|
|
|
|
).click()
|
|
|
|
|
self.wait_for(
|
|
|
|
|
lambda: self.browser.find_element(By.CSS_SELECTOR, "#id_kit_earthman_deck")
|
|
|
|
|
).click()
|
|
|
|
|
|
|
|
|
|
# DON is disabled
|
|
|
|
|
don_btn = self.wait_for(
|
|
|
|
|
lambda: self.browser.find_element(
|
|
|
|
|
By.CSS_SELECTOR, f"#id_kit_earthman_deck .btn-equip"
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
self.assertIn("btn-disabled", don_btn.get_attribute("class"))
|
|
|
|
|
|
|
|
|
|
# DOFF button is not present (not just disabled — entirely absent)
|
|
|
|
|
doff_btns = self.browser.find_elements(
|
|
|
|
|
By.CSS_SELECTOR, f"#id_kit_earthman_deck .btn-unequip:not(.btn-disabled)"
|
|
|
|
|
)
|
|
|
|
|
self.assertEqual(len(doff_btns), 0, "DOFF button should not be shown for an in-use deck")
|
|
|
|
|
|
|
|
|
|
def test_tooltip_names_the_game_for_in_use_deck(self):
|
|
|
|
|
"""Opening an in-use deck's tooltip shows the room name it is contributing to."""
|
|
|
|
|
gamer, earthman, room, seat = self._setup_in_use_deck()
|
|
|
|
|
session_key = self.create_pre_authenticated_session(GAMER_EMAIL)
|
|
|
|
|
self.browser.get(self.live_server_url)
|
|
|
|
|
self.browser.add_cookie({"name": "sessionid", "value": session_key})
|
2026-04-27 23:24:43 -04:00
|
|
|
self.browser.get(self.live_server_url + "/gameboard/")
|
2026-04-27 22:22:08 -04:00
|
|
|
self.wait_for(
|
|
|
|
|
lambda: self.browser.find_element(By.CSS_SELECTOR, "#id_kit_card_deck")
|
|
|
|
|
).click()
|
|
|
|
|
self.wait_for(
|
|
|
|
|
lambda: self.browser.find_element(By.CSS_SELECTOR, "#id_kit_earthman_deck")
|
|
|
|
|
).click()
|
|
|
|
|
game_label = self.wait_for(
|
|
|
|
|
lambda: self.browser.find_element(By.CSS_SELECTOR, ".tt-deck-game-name")
|
|
|
|
|
)
|
|
|
|
|
self.assertIn(room.name.upper(), game_label.text.upper())
|
|
|
|
|
|
|
|
|
|
def test_non_contributing_deck_has_normal_don_doff(self):
|
|
|
|
|
"""A deck not assigned to any active seat shows the normal DON/DOFF apparatus."""
|
|
|
|
|
gamer, earthman, room, seat = self._setup_in_use_deck()
|
|
|
|
|
# Unlock Fiorentine for the gamer so it appears in Game Kit
|
|
|
|
|
fiorentine = DeckVariant.objects.get(slug="fiorentine-minchiate")
|
|
|
|
|
gamer.unlocked_decks.add(fiorentine)
|
|
|
|
|
session_key = self.create_pre_authenticated_session(GAMER_EMAIL)
|
|
|
|
|
self.browser.get(self.live_server_url)
|
|
|
|
|
self.browser.add_cookie({"name": "sessionid", "value": session_key})
|
2026-04-27 23:24:43 -04:00
|
|
|
self.browser.get(self.live_server_url + "/gameboard/")
|
2026-04-27 22:22:08 -04:00
|
|
|
self.wait_for(
|
|
|
|
|
lambda: self.browser.find_element(By.CSS_SELECTOR, "#id_kit_card_deck")
|
|
|
|
|
).click()
|
|
|
|
|
self.wait_for(
|
|
|
|
|
lambda: self.browser.find_element(By.CSS_SELECTOR, "#id_kit_fiorentine_deck")
|
|
|
|
|
).click()
|
|
|
|
|
don_btn = self.wait_for(
|
|
|
|
|
lambda: self.browser.find_element(
|
|
|
|
|
By.CSS_SELECTOR, "#id_kit_fiorentine_deck .btn-equip"
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
self.assertNotIn("btn-disabled", don_btn.get_attribute("class"))
|