"""Functional tests for the DRAW SEA overlay — Celtic Cross draw.""" from django.test import tag from django.urls import reverse from django.utils import timezone from selenium.webdriver.common.by import By from apps.applets.models import Applet from apps.epic.models import Character, GateSlot, Room, TableSeat, TarotCard, DeckVariant from apps.lyric.models import User from .base import ChannelsFunctionalTest def _make_sky_confirmed_room(live_server_url, user, earthman): """Create a SKY_SELECT room with one gamer seated and sig assigned. Returns (room, seat). The Character is NOT yet confirmed — call _confirm_sky() in the browser to trigger the async transition. """ room = Room.objects.create( name="Sea Test Room", table_status=Room.SKY_SELECT, owner=user ) slot = room.gate_slots.get(slot_number=1) slot.gamer = user slot.status = GateSlot.FILLED slot.save() room.gate_status = Room.OPEN room.save() sig_card = TarotCard.objects.filter(deck_variant=earthman, arcana="MAJOR").first() seat = TableSeat.objects.create( room=room, gamer=user, role="PC", slot_number=1, deck_variant=earthman, significator=sig_card, ) return room, seat @tag("channels") class PickSeaAsyncTransitionTest(ChannelsFunctionalTest): """After sky confirm, the sky overlay closes and the room reloads to the table hex w. the DRAW SEA btn visible — the gamer must opt into the sea overlay rather than be auto-launched into it.""" def setUp(self): super().setUp() self.browser.set_window_size(800, 1200) Applet.objects.get_or_create( slug="new-game", defaults={"name": "New Game", "context": "gameboard"} ) Applet.objects.get_or_create( slug="my-games", defaults={"name": "My Games", "context": "gameboard"} ) from apps.lyric.models import User gamer, _ = User.objects.get_or_create(email="founder@test.io") earthman, _ = DeckVariant.objects.get_or_create( slug="earthman", defaults={"name": "Earthman Deck", "card_count": 108, "is_default": True}, ) gamer.unlocked_decks.add(earthman) gamer.equipped_deck = earthman gamer.save(update_fields=["equipped_deck"]) self.gamer = gamer self.room, self.seat = _make_sky_confirmed_room(self.live_server_url, gamer, earthman) self.room_url = self.live_server_url + reverse( "epic:room", kwargs={"room_id": self.room.id} ) self.sky_save_url = self.live_server_url + reverse( "epic:sky_save", kwargs={"room_id": self.room.id} ) def _confirm_sky(self): """POST to sky_save with action=confirm from browser JS (bypasses chart form).""" # Wait for the room WS connection to be ready before triggering confirm self.wait_for(lambda: self.browser.execute_script( "return !!(window._roomSocket && window._roomSocket.readyState === 1);" )) self.browser.execute_script(f""" const csrf = (document.cookie.match(/csrftoken=([^;]+)/) || ['',''])[1]; fetch('{self.sky_save_url}', {{ method: 'POST', credentials: 'same-origin', headers: {{'Content-Type': 'application/json', 'X-CSRFToken': csrf}}, body: JSON.stringify({{ birth_dt: '1990-06-15T09:00:00Z', birth_lat: 51.5, birth_lon: -0.1, birth_place: 'London', house_system: 'O', chart_data: {{}}, action: 'confirm', }}), }}); """) def test_pick_sea_btn_visible_after_sky_confirm(self): """Confirming sky reloads the room to the hex w. DRAW SEA replacing CAST SKY; the sea overlay is NOT auto-opened.""" self.create_pre_authenticated_session("founder@test.io") self.browser.get(self.room_url) # Sky not yet confirmed — CAST SKY btn present. self.wait_for(lambda: self.browser.find_element(By.ID, "id_pick_sky_btn")) self._confirm_sky() # Page reloads → hex shows DRAW SEA in place of CAST SKY. self.wait_for(lambda: self.browser.find_element(By.ID, "id_pick_sea_btn")) self.assertEqual(self.browser.find_elements(By.ID, "id_pick_sky_btn"), []) # Sea overlay is NOT auto-opened — it only appears once the gamer # clicks DRAW SEA. has_sea_open = self.browser.execute_script( "return document.documentElement.classList.contains('sea-open');" ) self.assertFalse(has_sea_open) def test_sky_overlay_closed_after_sky_confirm(self): """Sky overlay is gone (page reloaded) after sky confirm.""" self.create_pre_authenticated_session("founder@test.io") self.browser.get(self.room_url) self.wait_for(lambda: self.browser.find_element(By.ID, "id_pick_sky_btn")) self._confirm_sky() self.wait_for(lambda: self.browser.find_element(By.ID, "id_pick_sea_btn")) sky = self.browser.find_elements(By.ID, "id_sky_overlay") self.assertTrue(not sky or not sky[0].is_displayed()) def test_clicking_pick_sea_btn_opens_sea_overlay(self): """The gamer's explicit click on DRAW SEA is what opens the sea overlay.""" self.create_pre_authenticated_session("founder@test.io") self.browser.get(self.room_url) self.wait_for(lambda: self.browser.find_element(By.ID, "id_pick_sky_btn")) self._confirm_sky() self.wait_for(lambda: self.browser.find_element(By.ID, "id_pick_sea_btn")) # On slow CI, the DRAW SEA btn parses into the DOM before the inline # `