"""FTs for the My Sea standalone page sign-gate.
Sprint 4b of [[project-my-sea-roadmap]]. The /gameboard/my-sea/ page is
gated behind sig selection — when `user.significator` is None, render a
Look!-formatted Brief-style line w. FYI (→ /billboard/my-sign/) + BACK
(→ /gameboard/) instead of the draw UX. The My Sea applet on /gameboard/
mirrors the gate hint in its empty-state slot.
"""
from selenium.webdriver.common.by import By
from .base import FunctionalTest
from apps.applets.models import Applet
from apps.epic.models import personal_sig_cards
from apps.lyric.models import User
def _seed_gameboard_applets():
"""My Sea + the rest of the gameboard applets so /gameboard/ renders
without missing-applet errors during the applet-side assertions."""
for slug, name, cols, rows, ctx in [
("my-sea", "My Sea", 12, 4, "gameboard"),
("game-kit", "Game Kit", 4, 6, "gameboard"),
("my-palette", "My Palette", 4, 4, "gameboard"),
("my-games", "My Games", 4, 4, "gameboard"),
]:
Applet.objects.get_or_create(
slug=slug,
defaults={"name": name, "context": ctx,
"default_visible": True, "grid_cols": cols, "grid_rows": rows},
)
class MySeaSignGateTest(FunctionalTest):
"""Sign-gate UX on the standalone /gameboard/my-sea/ page + the
/gameboard/ My Sea applet. User without a saved sig sees a Look!-
formatted nudge w. FYI to the picker + BACK to the gameboard."""
serialized_rollback = True
def setUp(self):
super().setUp()
_seed_gameboard_applets()
self.email = "sea@test.io"
self.gamer = User.objects.create(email=self.email)
sig_pile = personal_sig_cards(self.gamer)
self.target_card = sig_pile[0] if sig_pile else None
self.assertIsNotNone(
self.target_card,
"personal_sig_cards(user) returned no cards — check Earthman seed",
)
# ── Test 1 ───────────────────────────────────────────────────────────────
def test_no_sig_renders_lookline_gate_on_standalone_page(self):
"""User without significator → /gameboard/my-sea/ shows the Look!-
formatted Brief-style line w. the gate copy + FYI + BACK buttons."""
self.create_pre_authenticated_session(self.email)
self.browser.get(self.live_server_url + "/gameboard/my-sea/")
gate = self.wait_for(
lambda: self.browser.find_element(
By.CSS_SELECTOR, ".my-sea-sign-gate"
)
)
text = gate.text
self.assertIn("Look!", text)
self.assertIn("pick your sign", text.lower())
self.assertIn("drawing the Sea", text)
# FYI + BACK action buttons
fyi = gate.find_element(By.CSS_SELECTOR, ".my-sea-sign-gate__fyi")
self.assertTrue(fyi.is_displayed())
back = gate.find_element(By.CSS_SELECTOR, ".my-sea-sign-gate__back")
self.assertTrue(back.is_displayed())
# ── Test 2 ───────────────────────────────────────────────────────────────
def test_gate_fyi_links_to_my_sign_picker(self):
"""FYI button is an `` pointing at /billboard/my-sign/."""
self.create_pre_authenticated_session(self.email)
self.browser.get(self.live_server_url + "/gameboard/my-sea/")
fyi = self.wait_for(
lambda: self.browser.find_element(
By.CSS_SELECTOR, ".my-sea-sign-gate__fyi"
)
)
href = fyi.get_attribute("href") or ""
self.assertTrue(
href.endswith("/billboard/my-sign/"),
f"FYI should link to /billboard/my-sign/, got {href!r}",
)
# ── Test 3 ───────────────────────────────────────────────────────────────
def test_gate_back_links_to_gameboard(self):
"""BACK button is an `` pointing at /gameboard/."""
self.create_pre_authenticated_session(self.email)
self.browser.get(self.live_server_url + "/gameboard/my-sea/")
back = self.wait_for(
lambda: self.browser.find_element(
By.CSS_SELECTOR, ".my-sea-sign-gate__back"
)
)
href = back.get_attribute("href") or ""
self.assertTrue(
href.endswith("/gameboard/"),
f"BACK should link to /gameboard/, got {href!r}",
)
# ── Test 4 ───────────────────────────────────────────────────────────────
def test_with_sig_skips_gate_and_renders_draw_shell(self):
"""User w. saved significator → no .my-sea-sign-gate on the page;
draw shell renders normally (Sprint 3 placeholder)."""
self.gamer.significator = self.target_card
self.gamer.save(update_fields=["significator"])
self.create_pre_authenticated_session(self.email)
self.browser.get(self.live_server_url + "/gameboard/my-sea/")
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".my-sea-page")
)
self.assertEqual(
len(self.browser.find_elements(By.CSS_SELECTOR, ".my-sea-sign-gate")),
0,
"Gate should not render when user has a saved significator",
)
# ── Test 5 ───────────────────────────────────────────────────────────────
def test_no_sig_applet_mirrors_gate_with_fyi_link(self):
"""On /gameboard/, the My Sea applet's empty state shows the same
Look!-formatted gate w. FYI link to /billboard/my-sign/ when the
user has no significator. Provides a consistent UX across surfaces."""
self.create_pre_authenticated_session(self.email)
self.browser.get(self.live_server_url + "/gameboard/")
applet_gate = self.wait_for(
lambda: self.browser.find_element(
By.CSS_SELECTOR, "#id_applet_my_sea .my-sea-sign-gate"
)
)
self.assertIn("Look!", applet_gate.text)
fyi = applet_gate.find_element(By.CSS_SELECTOR, ".my-sea-sign-gate__fyi")
href = fyi.get_attribute("href") or ""
self.assertTrue(href.endswith("/billboard/my-sign/"))
# ── Test 6 ───────────────────────────────────────────────────────────────
def test_with_sig_applet_renders_default_empty_state(self):
"""Applet w. saved sig → no gate, empty-state placeholder (until
Sprint 7 wires up the latest-draw rendering)."""
self.gamer.significator = self.target_card
self.gamer.save(update_fields=["significator"])
self.create_pre_authenticated_session(self.email)
self.browser.get(self.live_server_url + "/gameboard/")
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, "#id_applet_my_sea")
)
self.assertEqual(
len(self.browser.find_elements(
By.CSS_SELECTOR, "#id_applet_my_sea .my-sea-sign-gate"
)),
0,
)