My Sea DRAW SEA landing — Sprint 5 iter 1 of My Sea roadmap — TDD
DRAW SEA landing UX on /gameboard/my-sea/ for users past the [[sprint-my-sea-sign-gate-may19]] gate. DRY table hex (reused from the room shell + my-sign Sprint 4a iter 3) w. 6 chair seats labeled 1C-6C (placeholder for friend-invite per the My Sea roadmap "Six chairs retained even in solo" anchor) + central DRAW SEA `.btn-primary` mirroring SCAN SIGN on /billboard/my-sign/. Click swaps `.my-sea-page[data-phase]` from `landing` to `picker`; the picker UX itself (three-card cross w. cover/leave/loom + form col / spread dropdown / decks / LOCK HAND / DEL) lands in iters 2 + 3. The 'C' suffix on the chair labels = "Chair" (user-locked); no role semantics (this is a solo draw, not a 6-player role assignment). `.table-seat` CSS class + `data-slot` attribute preserved so the room's existing `[data-slot="N"]` positioning rules (`_room.scss` L583-588) carry over for free — no SCSS fork; just a new `.seat-label` span inside each seat. The 'Default deck warning' Brief banner from /billboard/my-sign/ fires verbatim when `user.equipped_deck` is None (the user is headed for a draw against the Earthman [Shabby Cardstock] backup unless they equip one first). Tagged `.my-sea-intro-banner` so FTs disambiguate from other Briefs. Same FYI (→ /gameboard/) / NVM (dismiss) action grammar. Bundled: BACK→NVM label swap (user-edited mid-sprint) in the existing sign-gate. CSS class `.my-sea-sign-gate__back` retained — the swap was label-only — so existing FTs targeting the class still pass; docstrings + comments updated for accuracy. Files: - `apps/gameboard/views.py` — `my_sea` view adds 2 context keys: `no_equipped_deck` (bool) + `show_backup_intro_banner` (= user_has_sig AND no_equipped_deck). The sig-gate path still wins precedence. - `templates/apps/gameboard/my_sea.html` — `.my-sea-page[data-phase="landing"]`; new `.my-sea-landing` block w. room-shell hex + `#id_draw_sea_btn` + 6 `.table-seat[data-slot="N"]` w. `<span class="seat-label">NC</span>`; new `.my-sea-picker` placeholder (`display:none` til DRAW SEA click); inline `<script>` for the click→data-phase swap + scaleTable re-fire on next tick (mirrors my-sign's iter-3 RAF dispatch); copies the my-sign Brief banner script block verbatim w. `.my-sea-intro-banner` post-render tag. - `static_src/scss/_gameboard.scss` — new `.my-sea-page` + `.my-sea-landing` + `.my-sea-picker` rule blocks mirroring `.my-sign-page` / `.my-sign-landing` from `_card-deck.scss`. `.seat-label` styled in `--terUser` to match the chair iconography. - `apps/gameboard/tests/integrated/test_views.py` — `+7 ITs` in new `MySeaDrawSeaLandingViewTest` pinning the context keys + presence/absence of `#id_draw_sea_btn` + 6 `data-slot=N` w. `NC` labels + Sprint 4b gate's precedence over the new landing. - `functional_tests/test_game_my_sea.py` — `+5 FTs` in new `MySeaDrawSeaLandingTest` for the visible UX (hex + DRAW SEA, 6 seats labeled 1C-6C, click→picker phase swap, Brief banner on no-deck, no banner when deck equipped). Uses new `_assign_sig` helper from [[sprint-sig-page-helper-may19c]] for the user-w-sig precondition. Tests: 25/25 FTs green across test_bill_my_sign + test_game_my_sea in 219s; 1036/1036 IT/UT green in 50s (+7 from baseline). Code architected by Disco DeDisco <discodedisco@outlook.com> Git commit message Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -512,3 +512,61 @@ class MySeaViewTest(TestCase):
|
||||
response = self.client.get(reverse("my_sea"))
|
||||
self.assertIn("page-gameboard", response.content.decode())
|
||||
self.assertIn("page-my-sea", response.content.decode())
|
||||
|
||||
|
||||
class MySeaDrawSeaLandingViewTest(TestCase):
|
||||
"""Sprint 5 iter 1 — view context for the DRAW SEA landing UX. Pins
|
||||
`no_equipped_deck` + `show_backup_intro_banner` context keys + the
|
||||
presence of the new landing template elements when user passes the
|
||||
Sprint 4b sign-gate."""
|
||||
|
||||
def setUp(self):
|
||||
from apps.epic.models import personal_sig_cards
|
||||
self.user = User.objects.create(email="draw@test.io")
|
||||
self.client.force_login(self.user)
|
||||
# Assign a sig so the view's landing branch (not the gate) renders.
|
||||
self.user.significator = personal_sig_cards(self.user)[0]
|
||||
self.user.save(update_fields=["significator"])
|
||||
|
||||
def test_context_no_equipped_deck_false_when_user_has_deck(self):
|
||||
# post_save auto-equips Earthman; `no_equipped_deck` should be False.
|
||||
response = self.client.get(reverse("my_sea"))
|
||||
self.assertFalse(response.context["no_equipped_deck"])
|
||||
|
||||
def test_context_no_equipped_deck_true_when_user_cleared_deck(self):
|
||||
self.user.equipped_deck = None
|
||||
self.user.save(update_fields=["equipped_deck"])
|
||||
response = self.client.get(reverse("my_sea"))
|
||||
self.assertTrue(response.context["no_equipped_deck"])
|
||||
|
||||
def test_context_show_backup_intro_banner_when_no_deck_and_has_sig(self):
|
||||
# Brief banner fires when user has a sig AND no deck — they're on the
|
||||
# landing UX (gate passed) but headed for the backup-deck draw path.
|
||||
self.user.equipped_deck = None
|
||||
self.user.save(update_fields=["equipped_deck"])
|
||||
response = self.client.get(reverse("my_sea"))
|
||||
self.assertTrue(response.context["show_backup_intro_banner"])
|
||||
|
||||
def test_context_show_backup_intro_banner_false_when_deck_equipped(self):
|
||||
response = self.client.get(reverse("my_sea"))
|
||||
self.assertFalse(response.context["show_backup_intro_banner"])
|
||||
|
||||
def test_landing_renders_draw_sea_btn_when_sig_set(self):
|
||||
response = self.client.get(reverse("my_sea"))
|
||||
self.assertContains(response, 'id="id_draw_sea_btn"')
|
||||
|
||||
def test_landing_renders_six_chair_seats_with_C_suffix(self):
|
||||
response = self.client.get(reverse("my_sea"))
|
||||
html = response.content.decode()
|
||||
for n in range(1, 7):
|
||||
with self.subTest(slot=n):
|
||||
self.assertIn(f'data-slot="{n}"', html)
|
||||
self.assertIn(f"{n}C", html)
|
||||
|
||||
def test_landing_not_rendered_when_user_has_no_sig(self):
|
||||
# Sprint 4b gate still wins precedence — DRAW SEA must not render
|
||||
# when significator is None.
|
||||
self.user.significator = None
|
||||
self.user.save(update_fields=["significator"])
|
||||
response = self.client.get(reverse("my_sea"))
|
||||
self.assertNotContains(response, 'id="id_draw_sea_btn"')
|
||||
|
||||
Reference in New Issue
Block a user