Files
python-tdd/src/functional_tests/test_gatekeeper.py

240 lines
10 KiB
Python
Raw Normal View History

import time
from selenium.webdriver.common.by import By
from .base import FunctionalTest
from apps.applets.models import Applet
from apps.epic.models import Room, GateSlot
from apps.lyric.models import User
class GatekeeperTest(FunctionalTest):
def setUp(self):
super().setUp()
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"}
)
def test_founder_creates_room_and_sees_gatekeeper(self):
# 1. Log in, navigate to gameboard
self.create_pre_authenticated_session("founder@test.io")
self.browser.get(self.live_server_url + "/gameboard/")
# 2. New Game applet has room name input, create button
self.wait_for(
lambda: self.browser.find_element(By.ID, "id_applet_new_game")
)
self.browser.find_element(By.ID, "id_new_game_name").send_keys("Test Room")
self.browser.find_element(By.ID, "id_create_game_btn").click()
# 3. User is redirected to Gatekeeper page for new room
self.wait_for(
lambda: self.assertIn("/gameboard/room/", self.browser.current_url)
)
self.wait_for(
lambda: self.assertIn("/gate/", self.browser.current_url)
)
# 4. Page shows room name, GATHERING status
body = self.browser.find_element(By.TAG_NAME, "body")
self.assertIn("Test Room", body.text)
self.assertIn("GATHERING", body.text)
# 5. Six token slots are visible
slots = self.browser.find_elements(By.CSS_SELECTOR, ".gate-slot")
self.assertEqual(len(slots), 6)
# 6. Slot 1 has Drop Token btn; slots 26 show as empty
slot_1 = slots[0]
slot_1.find_element(By.CSS_SELECTOR, ".drop-token-btn")
for slot in slots[1:]:
self.assertIn("empty", slot.get_attribute("class"))
def test_founder_drops_token_and_slot_fills(self):
# 1. Set up: log in, create room, arrive at gatekeeper
self.create_pre_authenticated_session("founder@test.io")
self.browser.get(self.live_server_url + "/gameboard/")
self.wait_for(
lambda: self.browser.find_element(By.ID, "id_new_game_name")
)
self.browser.find_element(By.ID, "id_new_game_name").send_keys("Dragon's Den")
self.browser.find_element(By.ID, "id_create_game_btn").click()
self.wait_for(
lambda: self.assertIn("/gate/", self.browser.current_url)
)
# 2. Founder clicks Drop Token on slot 1
drop_btn = self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".drop-token-btn")
)
drop_btn.click()
# 3. Slot 1 now filled; drop btn gone
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".gate-slot.filled")
)
slots = self.browser.find_elements(By.CSS_SELECTOR, ".gate-slot")
self.assertIn("filled", slots[0].get_attribute("class"))
self.assertEqual(
len(self.browser.find_elements(By.CSS_SELECTOR, ".drop-token-btn")), 0
)
def test_room_appears_in_my_games_after_creation(self):
# 1. Set up founder, game room, name
self.create_pre_authenticated_session("founder@test.io")
self.browser.get(self.live_server_url + "/gameboard/")
self.wait_for(
lambda: self.browser.find_element(By.ID, "id_new_game_name")
)
self.browser.find_element(By.ID, "id_new_game_name").send_keys("Dragon's Den")
self.browser.find_element(By.ID, "id_create_game_btn").click()
self.wait_for(
lambda: self.assertIn("/gate/", self.browser.current_url)
)
# 2. Navigate back to gameboard
self.browser.get(self.live_server_url + "/gameboard/")
my_games = self.wait_for(
lambda: self.browser.find_element(By.ID, "id_applet_my_games")
)
self.assertIn("Dragon's Den", my_games.text)
def test_second_gamer_drops_token_into_open_slot(self):
# 1. Founder creates room, fills slot 1
self.create_pre_authenticated_session("founder@test.io")
self.browser.get(self.live_server_url +"/gameboard/")
self.wait_for(
lambda: self.browser.find_element(By.ID, "id_new_game_name")
)
self.browser.find_element(By.ID, "id_new_game_name").send_keys("Dragon's Den")
self.browser.find_element(By.ID, "id_create_game_btn").click()
self.wait_for(
lambda: self.assertIn("/gate/", self.browser.current_url)
)
room_url = self.browser.current_url
self.browser.find_element(By.CSS_SELECTOR, ".drop-token-btn").click()
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".gate-slot.filled")
)
# 2. Founder invites friend via email (duplicate invite logic from My Notes applet)
invite_input = self.wait_for(
lambda: self.browser.find_element(By.ID, "id_invite_email")
)
invite_input.send_keys("friend@test.io")
self.browser.find_element(By.ID, "id_invite_btn").click()
# 3. Friend logs in, sees invitation in My Games
self.create_pre_authenticated_session("friend@test.io")
self.browser.get(self.live_server_url + "/gameboard/")
my_games = self.wait_for(
lambda: self.browser.find_element(By.ID, "id_applet_my_games")
)
self.assertIn("Dragon's Den", my_games.text)
# 3. Friend follows link to gatekeeper
self.browser.find_element(By.LINK_TEXT, "Dragon's Den").click()
# 4. Friend sees drop btn on open slot
drop_btn = self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".drop-token-btn")
)
drop_btn.click()
# 5. Now two slots filled
self.wait_for(
lambda: self.assertEqual(
len(self.browser.find_elements(By.CSS_SELECTOR, ".gate-slot.filled")), 2
)
)
def test_gate_opens_when_all_slots_filled(self):
# 1. Founder creates room
self.create_pre_authenticated_session("founder@test.io")
self.browser.get(self.live_server_url + "/gameboard/")
self.wait_for(
lambda: self.browser.find_element(By.ID, "id_new_game_name")
)
self.browser.find_element(By.ID, "id_new_game_name").send_keys("Dragon's Den")
self.browser.find_element(By.ID, "id_create_game_btn").click()
self.wait_for(
lambda: self.assertIn("/gate/", self.browser.current_url)
)
room_url = self.browser.current_url
# 2. Fill all 6 slots directly via ORM (founder + 5 extras)
self.browser.find_element(By.CSS_SELECTOR, ".drop-token-btn").click()
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".gate-slot.filled")
)
room = Room.objects.get(name="Dragon's Den")
for i, email in enumerate([
"g2@test.io", "g3@test.io", "g4@test.io", "g5@test.io", "g6@test.io"
], start=2):
gamer = User.objects.create(email=email)
slot = room.gate_slots.get(slot_number=i)
slot.gamer = gamer
slot.status = GateSlot.FILLED
slot.save()
room.refresh_from_db()
room.gate_status = Room.OPEN
room.save()
# 3. Gatekeeper disappears via htmx
self.wait_for(
lambda: self.assertEqual(
len(self.browser.find_elements(By.CSS_SELECTOR, ".gate-modal")), 0
)
)
# Restore the following once room built
# body = self.browser.find_element(By.TAG_NAME, "body")
# self.assertIn("OPEN", body.text)
def test_owner_can_delete_room_via_gear_menu(self):
self.create_pre_authenticated_session("founder@test.io")
self.browser.get(self.live_server_url + "/gameboard/")
self.wait_for(lambda: self.browser.find_element(By.ID, "id_new_game_name"))
self.browser.find_element(By.ID, "id_new_game_name").send_keys("Doomed Room")
self.browser.find_element(By.ID, "id_create_game_btn").click()
self.wait_for(lambda: self.assertIn("/gate/", self.browser.current_url))
self.browser.find_element(By.CSS_SELECTOR, ".gear-btn").click()
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".btn-danger")
).click()
self.wait_for(lambda: self.assertEqual(
self.browser.current_url, self.live_server_url + "/gameboard/"
))
self.assertFalse(Room.objects.filter(name="Doomed Room").exists())
def test_gatekeeper_overlay_persists_after_htmx_poll(self):
# 1. Create room directly (GATHERING) and navigate to its gate URL
self.create_pre_authenticated_session("founder@test.io")
founder = User.objects.get(email="founder@test.io")
room = Room.objects.create(name="Persistent Room", owner=founder)
self.browser.get(self.live_server_url + f"/gameboard/room/{room.id}/gate/")
# 2. Assert overlay visible on initial page load
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".gate-overlay")
)
# 3. Wait for HTMX poll cycle to fire (poll interval is 3s)
time.sleep(4)
# 4. Assert overlay still present and visible after poll
overlays = self.browser.find_elements(By.CSS_SELECTOR, ".gate-overlay")
self.assertEqual(len(overlays), 1)
self.assertTrue(overlays[0].is_displayed())
def test_gamer_can_abandon_room_via_gear_menu(self):
founder = User.objects.create(email="founder@test.io")
room = Room.objects.create(name="Dragon's Den", owner=founder)
slot = room.gate_slots.get(slot_number=2)
self.create_pre_authenticated_session("gamer@test.io")
gamer, _ = User.objects.get_or_create(email="gamer@test.io")
slot.gamer = gamer
slot.status = "FILLED"
slot.save()
self.browser.get(self.live_server_url + f"/gameboard/room/{room.id}/gate/")
self.browser.find_element(By.CSS_SELECTOR, ".gear-btn").click()
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".btn-abandon")
).click()
self.wait_for(lambda: self.assertEqual(
self.browser.current_url, self.live_server_url + "/gameboard/"
))
slot.refresh_from_db()
self.assertEqual(slot.status, "EMPTY")
self.assertIsNone(slot.gamer)