my_sea_visit nav: phase-aware NVM (hex→bud page, draw→hex) + navbar GATE VIEW → visit gate + guard reposition on resize — TDD

Spectator guard-portal + NVM/GATE VIEW routing (user-spec 2026-05-30):
- The leave-the-sea guard now confirms with OK (was NVM); `_my_sea_gear.html`
  gains an `nvm_handler` param so a caller can swap the default leave-nav.
- my_sea_visit NVM is phase-aware (`mySeaVisitNvm`): on the DRAW/spread phase it
  flips back to the table hex (client toggle, stays in-ecosphere → no voice
  guard, mirroring the owner's picker→landing); on the table hex it LEAVES to the
  bud's page (`billboard:bud_page`) behind the shared voice-disconnect guard.
- The navbar GATE VIEW opens THIS owner's visitor gatekeeper on my_sea_visit (+
  its gate page, whose page_class also carries `page-my-sea-visit`), not the
  viewer's own sea gate; owner pages are unchanged.
- showGuard now RE-POSITIONS the open guard on resize/orientationchange
  (rAF-throttled) so it follows its anchor instead of stranding at its show-time
  coords — a cross-cutting fix (every gear-menu guard) for the portal landing
  off-screen after an orientation flip relocated the gear menu.

Coverage: MySeaVisitNavTest (navbar→visit gate, gear NVM→mySeaVisitNvm, bud URL,
OK label) + MySeaOwnerNavbarGateUnaffectedTest (owner gate untouched). Verified
live in Firefox: picker NVM returns to the hex; the guard followed its anchor
from 882px→54px on a simulated orientation flip, staying in-viewport.

Code architected by Disco DeDisco <discodedisco@outlook.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-05-30 02:15:23 -04:00
parent 571d5a84ae
commit 7e39740f9c
5 changed files with 144 additions and 22 deletions

View File

@@ -257,6 +257,61 @@ class MySeaVisitEmptyHandLabelsTest(TestCase):
f'data-position="{caption.lower()}">{caption}</span>', html)
class MySeaVisitNavTest(TestCase):
"""NVM + GATE VIEW routing on the spectator surface (user-spec 2026-05-30):
the navbar GATE VIEW opens THIS owner's visitor gate (not the viewer's own);
the gear NVM is phase-aware (mySeaVisitNvm) — table hex → the bud's page,
draw phase → back to the hex; the leave-the-sea guard confirms with OK."""
def setUp(self):
self.owner = _owner_with_sig()
self.bud = User.objects.create(email="bud@test.io", username="budster")
SeaInvite.objects.create(
owner=self.owner, invitee=self.bud, invitee_email=self.bud.email,
status=SeaInvite.ACCEPTED, accepted_at=timezone.now(),
token_deposited_at=timezone.now(),
voice_until=timezone.now() + timedelta(hours=24),
)
self.client.force_login(self.bud)
self.html = self.client.get(
reverse("my_sea_visit", args=[self.owner.id])).content.decode()
def test_navbar_gate_view_targets_the_visit_gate_not_own(self):
visit_gate = reverse("my_sea_visit_gate", args=[self.owner.id])
self.assertIn(f"window.location.href='{visit_gate}'", self.html)
# The viewer's OWN sea gate must NOT be the navbar target here.
self.assertNotIn(
f"window.location.href='{reverse('my_sea_gate')}'", self.html)
def test_gear_nvm_uses_the_phase_aware_visit_handler(self):
self.assertIn('onclick="mySeaVisitNvm(event)"', self.html)
def test_visit_nvm_leaves_to_the_bud_page_from_the_hex(self):
bud_url = reverse("billboard:bud_page", args=[self.owner.id])
self.assertIn(f"BUD_URL = '{bud_url}'", self.html)
def test_leave_guard_confirms_with_OK(self):
self.assertIn("yesLabel: 'OK'", self.html)
self.assertNotIn("yesLabel: 'NVM'", self.html)
class MySeaOwnerNavbarGateUnaffectedTest(TestCase):
"""Regression — the OWNER's own my_sea navbar GATE VIEW still routes to the
user's own gate (the visit-gate branch must not leak onto owner pages)."""
def setUp(self):
from apps.epic.models import personal_sig_cards
self.user = User.objects.create(email="owner@test.io", username="discoself")
self.user.significator = personal_sig_cards(self.user)[0]
self.user.save(update_fields=["significator"])
self.client.force_login(self.user)
def test_owner_navbar_gate_targets_own_gate(self):
html = self.client.get(reverse("my_sea")).content.decode()
self.assertIn(
f"window.location.href='{reverse('my_sea_gate')}'", html)
class MySeaVisitOwnerSeatedTest(TestCase):
"""Phase 2 — the owner shows seated in 1C on the spectator hex whenever
she's committed to a draw cycle (drawn OR paid), not only once a card