My Sea picker phase: three-card cross (sig + cover/leave/loom) — Sprint 5 iter 2 of My Sea roadmap — TDD
After the FREE DRAW click on iter 1's landing swaps `data-phase` to `picker`, the picker now renders a stripped Celtic Cross: user's saved significator pinned in `.sea-pos-core`, three drawn-card drop zones around it — cover (overlaid on sig), leave (left of core), loom (right of core). Crown / lay / cross from the gameroom's 6-position spread are deliberately forsaken (user-locked spec).
DRY w. the gameroom sea-overlay: reuses `.sea-cards-col` + `.sea-cross` + `.sea-crucifix-cell` + `.sea-pos-*` + `.sea-card-slot--empty` + `.sea-sig-card` classes & their _card-deck.scss styling (1181-1331). Only divergence from the room: a `.my-sea-cross` modifier in `_gameboard.scss` overrides `grid-template-areas` from the room's `". crown . / leave core loom / . lay ."` 3×3 to a single-row `"leave core loom"` — drops the crown + lay rows since those positions are forsaken. Cover stays nested inside `.sea-pos-core` so the absolute-overlay rules from _card-deck.scss line 1310-1331 carry over for free.
Picker bg = `rgba(var(--duoUser), 1)` on `.my-sea-page[data-phase="picker"]` — parallels `.my-sign-page[data-phase="picker"]` from _card-deck.scss line 704, so the landing→picker swap reads as a continuous surface (hex face → felt) like on /billboard/my-sign/.
The sig card renders w. `data-card-id="{{ significator.id }}"` + `.fan-corner-rank` + `.fa-solid {suit-icon}` (mirrors the gameroom's `.sea-sig-card` minimal markup at `_sea_overlay.html` line 33-39). Full card-face / FYI / SPIN wiring deferred — iter 3 lands the form col + interactive draw flow.
View context: `my_sea` now passes `significator` (FK pass-through) + `significator_reversed` so the template can render the corner rank + suit icon at render time without re-fetching.
- 3 FTs in new `MySeaPickerPhaseTest`: sig card w. `data-card-id` matching `user.significator.id` in `.sea-pos-core`; cover/leave/loom empty drop zones render; crown/lay/cross absent. Shared `_enter_picker_phase()` helper polls for `data-phase='picker'` after the ~800ms seat-1C animation delay.
- 4 ITs in new `MySeaPickerPhaseTemplateTest`: server-render contract for sig in core + cover/leave/loom classes + forsaken-positions-absent + picker entirely absent when user has no sig (4b gate precedence).
Tests: 28/28 FT green across test_bill_my_sign + test_game_my_sea (~219s); 1041/1041 IT/UT green (53s).
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:
@@ -326,3 +326,88 @@ class MySeaDrawSeaLandingTest(FunctionalTest):
|
||||
len(self.browser.find_elements(By.CSS_SELECTOR, ".my-sea-intro-banner")),
|
||||
0,
|
||||
)
|
||||
|
||||
|
||||
class MySeaPickerPhaseTest(FunctionalTest):
|
||||
"""Sprint 5 iter 2 — picker phase content on /gameboard/my-sea/ after
|
||||
FREE DRAW click swaps `data-phase` to `picker`. Three-card spread:
|
||||
user's saved significator pinned in the center (`.sea-pos-core`) +
|
||||
three drawn-card positions surrounding it — cover (overlaid on sig),
|
||||
leave (left of center), loom (right of center). Crown / lay / cross
|
||||
from the gameroom's 6-position Celtic Cross are deliberately omitted
|
||||
(user-locked spec). Empty drop-zones are visible — actual card-draw
|
||||
wiring lands in iter 3 alongside the form col (spread dropdown /
|
||||
decks / LOCK HAND / DEL)."""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
_seed_earthman_sig_pile()
|
||||
_seed_gameboard_applets()
|
||||
self.email = "picker@test.io"
|
||||
self.gamer = User.objects.create(email=self.email)
|
||||
self.target_card = _assign_sig(self.gamer)
|
||||
|
||||
def _enter_picker_phase(self):
|
||||
"""Common nav: load /gameboard/my-sea/, click FREE DRAW, wait for
|
||||
the page wrapper's data-phase to swap to `picker` (which happens
|
||||
~800ms after click per the seat-1C animation delay in the inline
|
||||
JS)."""
|
||||
self.create_pre_authenticated_session(self.email)
|
||||
self.browser.get(self.live_server_url + "/gameboard/my-sea/")
|
||||
btn = self.wait_for(
|
||||
lambda: self.browser.find_element(By.CSS_SELECTOR, "#id_draw_sea_btn")
|
||||
)
|
||||
btn.click()
|
||||
return self.wait_for(
|
||||
lambda: self.browser.find_element(
|
||||
By.CSS_SELECTOR, ".my-sea-page[data-phase='picker']"
|
||||
)
|
||||
)
|
||||
|
||||
# ── Test 1 ───────────────────────────────────────────────────────────────
|
||||
|
||||
def test_picker_renders_significator_card_in_core_cell(self):
|
||||
"""User's saved significator pins the `.sea-pos-core` cell — the
|
||||
center of the three-card cross. Card data attribute reflects the
|
||||
actual TarotCard.id so future iters can wire FYI / SPIN onto it."""
|
||||
self._enter_picker_phase()
|
||||
core = self.browser.find_element(
|
||||
By.CSS_SELECTOR, ".my-sea-picker .sea-pos-core .sea-sig-card"
|
||||
)
|
||||
self.assertEqual(
|
||||
core.get_attribute("data-card-id"), str(self.target_card.id)
|
||||
)
|
||||
|
||||
# ── Test 2 ───────────────────────────────────────────────────────────────
|
||||
|
||||
def test_picker_renders_cover_leave_loom_positions(self):
|
||||
"""The three drawn-card positions (cover/leave/loom) render as
|
||||
empty `.sea-card-slot--empty` drop zones. Cover is overlaid on
|
||||
the sig card via `.sea-pos-core > .sea-pos-cover` nesting; leave
|
||||
+ loom sit in their own grid cells flanking core."""
|
||||
picker = self._enter_picker_phase()
|
||||
# Cover lives nested inside .sea-pos-core (overlaid on sig)
|
||||
picker.find_element(
|
||||
By.CSS_SELECTOR, ".sea-pos-core .sea-pos-cover .sea-card-slot--empty"
|
||||
)
|
||||
picker.find_element(
|
||||
By.CSS_SELECTOR, ".sea-pos-leave .sea-card-slot--empty"
|
||||
)
|
||||
picker.find_element(
|
||||
By.CSS_SELECTOR, ".sea-pos-loom .sea-card-slot--empty"
|
||||
)
|
||||
|
||||
# ── Test 3 ───────────────────────────────────────────────────────────────
|
||||
|
||||
def test_picker_does_not_render_forsaken_positions(self):
|
||||
"""Crown / lay / cross from the gameroom's 6-position Celtic
|
||||
Cross are forsaken in the solo three-card spread (user-locked
|
||||
spec). None of these classes should appear in the picker DOM."""
|
||||
picker = self._enter_picker_phase()
|
||||
for forsaken in (".sea-pos-crown", ".sea-pos-lay", ".sea-pos-cross"):
|
||||
with self.subTest(position=forsaken):
|
||||
self.assertEqual(
|
||||
len(picker.find_elements(By.CSS_SELECTOR, forsaken)),
|
||||
0,
|
||||
f"forsaken position {forsaken} should not render in my-sea picker",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user