btn-primary label renames + stage-card polarity color refinements — two interleaved threads from one session, committing together since both touch sig + sea stage cards ; LABEL RENAMES: PICK SIGS → SCAN SIGS (room.html #id_pick_sigs_btn), PICK SKY → CAST SKY (room.html #id_pick_sky_btn × 2), PICK SEA → DRAW SEA (room.html #id_pick_sea_btn), TAKE SIG → SAVE SIG (sig-select.js _takeSigBtn.textContent × 2 callsites + section comment) — Element IDs (id_pick_sky_btn etc.), URL names (epic:pick_sigs, epic:pick_sky), and Python state enums (TableStatus.PICK_SKY, PICK_SEA, SIG_SELECT) intentionally retained as stable identifiers; the renamed text is purely the .btn-primary user-facing label ; FT + IT mentions of the old labels swept in test_game_room_select_{sig,sky,sea,role}.py, test_billboard.py, setup_sea_session.py mgmt cmd, apps/epic/{views,utils,models,tasks,tests/integrated/test_views}.py, SigSelectSpec.js, sky_overlay/sea_overlay/dashboard/sky.html, _card-deck.scss, _sky.scss — all docstring/comment references updated for cascade-grep cleanliness ; STAGE-CARD COLOR + CLASS REFINEMENTS (earlier in session): sig-stage card text colour split per polarity — gravity gets --terUser on .fan-card-name + .fan-card-reversal-{name,qualifier} + .sig-qualifier-{above,below}, levity gets --quiUser on the same five slots; all selectors prefixed w. .sig-stage-card to match the 0,4,0 specificity of the default `.sig-stage .sig-stage-card .fan-card-face .sig-qualifier-*` rule (without the prefix the polarity overrides lose the cascade — .sig-qualifier-below was visibly stuck on the default --quiUser) ; .stat-face-label gets polarity-inverse colours — gravity stat-block bg is --secUser (opposite of card's --priUser) so the label takes --quiUser to stay legible; levity is the symmetric flip (label = --terUser on --priUser stat-block bg) ; levity card title/qualifier drop-shadow swapped from rgba(0,0,0,…) → rgba(255,255,255,…) — dark drop reads as harsh smudge against the inverted-frame levity --secUser bg; applied to both sig-overlay[data-polarity="levity"] stage card AND sea-stage--levity via $_sea-title-shadow-levity (former shared $_sea-title-shadow split into per-polarity {levity,gravity} variants) ; reversal-face class/content alignment so each `.fan-card-reversal-*` class always carries its semantic content — DOM order per arcana type controls visual layout after the 180° SPIN (DOM-second appears visually on top): Major → title in .fan-card-reversal-name @ DOM-second (visually top after spin), qualifier in .fan-card-reversal-qualifier @ DOM-first; Non-major → title in .fan-card-reversal-name @ DOM-first (visually bottom after spin), qualifier in .fan-card-reversal-qualifier @ DOM-second (preserves the original "qualifier word reads first after spin" layout for Middle/Minor arcana — e.g. "Relieving / Eight of Crowns" not "Eight of Crowns / Relieving") ; _tarot_fan.html renders per-arcana DOM order directly (Django template branches handle both layouts); sig + sea overlays render a fixed two-`<p>` skeleton (one DOM order) so stage-card.js's populator dynamically rewrites the two `<p>`s' className per arcana — Major/override branch flips DOM-second to .fan-card-reversal-name + content, DOM-first to .fan-card-reversal-qualifier; non-major branch keeps DOM-first as .fan-card-reversal-name + title, DOM-second as .fan-card-reversal-qualifier + reversalQualifier-or-polarity-fallback ; SigSelectSpec.js + SeaDealSpec.js fixtures + Major reversed-face assertion updated for the new semantic — TDD
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 00:25:10 -04:00
|
|
|
"""Functional tests for the DRAW SEA overlay — Celtic Cross draw."""
|
2026-04-28 22:16:38 -04:00
|
|
|
|
2026-04-29 11:27:51 -04:00
|
|
|
from django.test import tag
|
2026-04-28 22:16:38 -04:00
|
|
|
from django.urls import reverse
|
2026-04-28 22:50:39 -04:00
|
|
|
from django.utils import timezone
|
2026-04-28 22:16:38 -04:00
|
|
|
from selenium.webdriver.common.by import By
|
|
|
|
|
|
|
|
|
|
from apps.applets.models import Applet
|
2026-04-28 22:50:39 -04:00
|
|
|
from apps.epic.models import Character, GateSlot, Room, TableSeat, TarotCard, DeckVariant
|
|
|
|
|
from apps.lyric.models import User
|
2026-04-28 22:16:38 -04:00
|
|
|
|
|
|
|
|
from .base import ChannelsFunctionalTest
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _make_sky_confirmed_room(live_server_url, user, earthman):
|
|
|
|
|
"""Create a SKY_SELECT room with one gamer seated and sig assigned.
|
|
|
|
|
|
|
|
|
|
Returns (room, seat). The Character is NOT yet confirmed — call
|
|
|
|
|
_confirm_sky() in the browser to trigger the async transition.
|
|
|
|
|
"""
|
|
|
|
|
room = Room.objects.create(
|
|
|
|
|
name="Sea Test Room", table_status=Room.SKY_SELECT, owner=user
|
|
|
|
|
)
|
|
|
|
|
slot = room.gate_slots.get(slot_number=1)
|
|
|
|
|
slot.gamer = user
|
|
|
|
|
slot.status = GateSlot.FILLED
|
|
|
|
|
slot.save()
|
|
|
|
|
room.gate_status = Room.OPEN
|
|
|
|
|
room.save()
|
|
|
|
|
|
|
|
|
|
sig_card = TarotCard.objects.filter(deck_variant=earthman, arcana="MAJOR").first()
|
|
|
|
|
seat = TableSeat.objects.create(
|
|
|
|
|
room=room, gamer=user, role="PC", slot_number=1,
|
|
|
|
|
deck_variant=earthman, significator=sig_card,
|
|
|
|
|
)
|
|
|
|
|
return room, seat
|
|
|
|
|
|
|
|
|
|
|
2026-04-29 11:27:51 -04:00
|
|
|
@tag("channels")
|
2026-04-28 22:16:38 -04:00
|
|
|
class PickSeaAsyncTransitionTest(ChannelsFunctionalTest):
|
rename natus → sky across the codebase — natal chart abstraction is now sky throughout, since chart inputs aren't birthday-gated
Mechanical rename: 5 files (sky-wheel.js, _sky.scss, _sky_overlay.html, SkyWheelSpec.js x2), 24 in-place edits across templates/views/urls/SCSS/JS/tests/CLAUDE.md. URL names epic:natus_save → epic:sky_save (epic namespaced, no clash w. dashboard:sky_save), JS module NatusWheel → SkyWheel, DOM ids id_natus_* → id_sky_*, BEM classes natus-* → sky-*, dashboard sky_natus_data/sky_natus_preview collapsed to sky_data/sky_preview_data. No DB migration needed (User.sky_chart_data + GameEvent.SKY_SAVED already used sky-prefix). 778 ITs + Jasmine green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 20:36:15 -04:00
|
|
|
"""After sky confirm, the sky overlay closes and the room reloads to the
|
btn-primary label renames + stage-card polarity color refinements — two interleaved threads from one session, committing together since both touch sig + sea stage cards ; LABEL RENAMES: PICK SIGS → SCAN SIGS (room.html #id_pick_sigs_btn), PICK SKY → CAST SKY (room.html #id_pick_sky_btn × 2), PICK SEA → DRAW SEA (room.html #id_pick_sea_btn), TAKE SIG → SAVE SIG (sig-select.js _takeSigBtn.textContent × 2 callsites + section comment) — Element IDs (id_pick_sky_btn etc.), URL names (epic:pick_sigs, epic:pick_sky), and Python state enums (TableStatus.PICK_SKY, PICK_SEA, SIG_SELECT) intentionally retained as stable identifiers; the renamed text is purely the .btn-primary user-facing label ; FT + IT mentions of the old labels swept in test_game_room_select_{sig,sky,sea,role}.py, test_billboard.py, setup_sea_session.py mgmt cmd, apps/epic/{views,utils,models,tasks,tests/integrated/test_views}.py, SigSelectSpec.js, sky_overlay/sea_overlay/dashboard/sky.html, _card-deck.scss, _sky.scss — all docstring/comment references updated for cascade-grep cleanliness ; STAGE-CARD COLOR + CLASS REFINEMENTS (earlier in session): sig-stage card text colour split per polarity — gravity gets --terUser on .fan-card-name + .fan-card-reversal-{name,qualifier} + .sig-qualifier-{above,below}, levity gets --quiUser on the same five slots; all selectors prefixed w. .sig-stage-card to match the 0,4,0 specificity of the default `.sig-stage .sig-stage-card .fan-card-face .sig-qualifier-*` rule (without the prefix the polarity overrides lose the cascade — .sig-qualifier-below was visibly stuck on the default --quiUser) ; .stat-face-label gets polarity-inverse colours — gravity stat-block bg is --secUser (opposite of card's --priUser) so the label takes --quiUser to stay legible; levity is the symmetric flip (label = --terUser on --priUser stat-block bg) ; levity card title/qualifier drop-shadow swapped from rgba(0,0,0,…) → rgba(255,255,255,…) — dark drop reads as harsh smudge against the inverted-frame levity --secUser bg; applied to both sig-overlay[data-polarity="levity"] stage card AND sea-stage--levity via $_sea-title-shadow-levity (former shared $_sea-title-shadow split into per-polarity {levity,gravity} variants) ; reversal-face class/content alignment so each `.fan-card-reversal-*` class always carries its semantic content — DOM order per arcana type controls visual layout after the 180° SPIN (DOM-second appears visually on top): Major → title in .fan-card-reversal-name @ DOM-second (visually top after spin), qualifier in .fan-card-reversal-qualifier @ DOM-first; Non-major → title in .fan-card-reversal-name @ DOM-first (visually bottom after spin), qualifier in .fan-card-reversal-qualifier @ DOM-second (preserves the original "qualifier word reads first after spin" layout for Middle/Minor arcana — e.g. "Relieving / Eight of Crowns" not "Eight of Crowns / Relieving") ; _tarot_fan.html renders per-arcana DOM order directly (Django template branches handle both layouts); sig + sea overlays render a fixed two-`<p>` skeleton (one DOM order) so stage-card.js's populator dynamically rewrites the two `<p>`s' className per arcana — Major/override branch flips DOM-second to .fan-card-reversal-name + content, DOM-first to .fan-card-reversal-qualifier; non-major branch keeps DOM-first as .fan-card-reversal-name + title, DOM-second as .fan-card-reversal-qualifier + reversalQualifier-or-polarity-fallback ; SigSelectSpec.js + SeaDealSpec.js fixtures + Major reversed-face assertion updated for the new semantic — TDD
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 00:25:10 -04:00
|
|
|
table hex w. the DRAW SEA btn visible — the gamer must opt into the sea
|
SAVE SKY provenance + sky→hex (not sky→sea) transition — TDD
- drama.GameEvent.SKY_SAVED verb + to_prose branch: "X beholds the skyscape of {poss} birth, which yields {obj} a unique {Cap} capacity."; tied highest scores switch "a unique" → "equal", join w. "and" (2-way) or Oxford comma (3+), and pluralize "capacity" → "capacities"; pronouns resolved from actor.pronouns at render time, same machinery as SIG_READY/ROLE_SELECTED
- epic.utils.ELEMENT_CAPACITOR_NAMES + ELEMENT_ORDER + top_capacitors(elements) helper: maps Fire→Ardor Stone→Ossum Time→Tempo Space→Nexus Air→Pneuma Water→Humor; tolerates both flat-int and enriched-dict (`{count, contributors}`) chart_data shapes; returns capacitor names tied for highest count, ordered by canonical wheel ring
- epic.natus_save: on action=confirm, records GameEvent.SKY_SAVED w. top_capacitors=[…] before _notify_sky_confirmed; per-room billscroll AND billboard Most Recent Scroll pick up the new prose
- _natus_overlay.html _onSkyConfirmed: removed sea-partial fetch+inject; now calls closeNatus() + window.location.reload() so the gamer lands on the table hex w. the PICK SKY → PICK SEA btn swap (server-side, driven by sky_confirmed=True), then opts into the sea overlay manually. The auto-launch via 39e12d6 was buried by FTs that were pinning the wrong contract — gamer never had a chance to witness PICK SEA on the hex
- test_room_sea_select.py: three FTs renamed/rewired from auto-launch assertions (sea_overlay_appears_without_page_refresh, natus_overlay_not_visible_after_sky_confirm, sea_open_class_on_html_after_confirm) to (pick_sea_btn_visible_after_sky_confirm, natus_overlay_closed_after_sky_confirm, clicking_pick_sea_btn_opens_sea_overlay) — sea overlay now requires explicit PICK SEA click
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 01:57:35 -04:00
|
|
|
overlay rather than be auto-launched into it."""
|
2026-04-28 22:16:38 -04:00
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
super().setUp()
|
|
|
|
|
self.browser.set_window_size(800, 1200)
|
|
|
|
|
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"}
|
|
|
|
|
)
|
|
|
|
|
from apps.lyric.models import User
|
|
|
|
|
gamer, _ = User.objects.get_or_create(email="founder@test.io")
|
|
|
|
|
earthman, _ = DeckVariant.objects.get_or_create(
|
|
|
|
|
slug="earthman",
|
|
|
|
|
defaults={"name": "Earthman Deck", "card_count": 108, "is_default": True},
|
|
|
|
|
)
|
|
|
|
|
gamer.unlocked_decks.add(earthman)
|
|
|
|
|
gamer.equipped_deck = earthman
|
|
|
|
|
gamer.save(update_fields=["equipped_deck"])
|
|
|
|
|
|
|
|
|
|
self.gamer = gamer
|
|
|
|
|
self.room, self.seat = _make_sky_confirmed_room(self.live_server_url, gamer, earthman)
|
|
|
|
|
self.room_url = self.live_server_url + reverse(
|
|
|
|
|
"epic:room", kwargs={"room_id": self.room.id}
|
|
|
|
|
)
|
rename natus → sky across the codebase — natal chart abstraction is now sky throughout, since chart inputs aren't birthday-gated
Mechanical rename: 5 files (sky-wheel.js, _sky.scss, _sky_overlay.html, SkyWheelSpec.js x2), 24 in-place edits across templates/views/urls/SCSS/JS/tests/CLAUDE.md. URL names epic:natus_save → epic:sky_save (epic namespaced, no clash w. dashboard:sky_save), JS module NatusWheel → SkyWheel, DOM ids id_natus_* → id_sky_*, BEM classes natus-* → sky-*, dashboard sky_natus_data/sky_natus_preview collapsed to sky_data/sky_preview_data. No DB migration needed (User.sky_chart_data + GameEvent.SKY_SAVED already used sky-prefix). 778 ITs + Jasmine green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 20:36:15 -04:00
|
|
|
self.sky_save_url = self.live_server_url + reverse(
|
|
|
|
|
"epic:sky_save", kwargs={"room_id": self.room.id}
|
2026-04-28 22:16:38 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def _confirm_sky(self):
|
rename natus → sky across the codebase — natal chart abstraction is now sky throughout, since chart inputs aren't birthday-gated
Mechanical rename: 5 files (sky-wheel.js, _sky.scss, _sky_overlay.html, SkyWheelSpec.js x2), 24 in-place edits across templates/views/urls/SCSS/JS/tests/CLAUDE.md. URL names epic:natus_save → epic:sky_save (epic namespaced, no clash w. dashboard:sky_save), JS module NatusWheel → SkyWheel, DOM ids id_natus_* → id_sky_*, BEM classes natus-* → sky-*, dashboard sky_natus_data/sky_natus_preview collapsed to sky_data/sky_preview_data. No DB migration needed (User.sky_chart_data + GameEvent.SKY_SAVED already used sky-prefix). 778 ITs + Jasmine green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 20:36:15 -04:00
|
|
|
"""POST to sky_save with action=confirm from browser JS (bypasses chart form)."""
|
2026-04-28 22:16:38 -04:00
|
|
|
# Wait for the room WS connection to be ready before triggering confirm
|
|
|
|
|
self.wait_for(lambda: self.browser.execute_script(
|
|
|
|
|
"return !!(window._roomSocket && window._roomSocket.readyState === 1);"
|
|
|
|
|
))
|
|
|
|
|
self.browser.execute_script(f"""
|
|
|
|
|
const csrf = (document.cookie.match(/csrftoken=([^;]+)/) || ['',''])[1];
|
rename natus → sky across the codebase — natal chart abstraction is now sky throughout, since chart inputs aren't birthday-gated
Mechanical rename: 5 files (sky-wheel.js, _sky.scss, _sky_overlay.html, SkyWheelSpec.js x2), 24 in-place edits across templates/views/urls/SCSS/JS/tests/CLAUDE.md. URL names epic:natus_save → epic:sky_save (epic namespaced, no clash w. dashboard:sky_save), JS module NatusWheel → SkyWheel, DOM ids id_natus_* → id_sky_*, BEM classes natus-* → sky-*, dashboard sky_natus_data/sky_natus_preview collapsed to sky_data/sky_preview_data. No DB migration needed (User.sky_chart_data + GameEvent.SKY_SAVED already used sky-prefix). 778 ITs + Jasmine green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 20:36:15 -04:00
|
|
|
fetch('{self.sky_save_url}', {{
|
2026-04-28 22:16:38 -04:00
|
|
|
method: 'POST',
|
|
|
|
|
credentials: 'same-origin',
|
|
|
|
|
headers: {{'Content-Type': 'application/json', 'X-CSRFToken': csrf}},
|
|
|
|
|
body: JSON.stringify({{
|
|
|
|
|
birth_dt: '1990-06-15T09:00:00Z',
|
|
|
|
|
birth_lat: 51.5, birth_lon: -0.1,
|
|
|
|
|
birth_place: 'London', house_system: 'O',
|
|
|
|
|
chart_data: {{}}, action: 'confirm',
|
|
|
|
|
}}),
|
|
|
|
|
}});
|
|
|
|
|
""")
|
|
|
|
|
|
SAVE SKY provenance + sky→hex (not sky→sea) transition — TDD
- drama.GameEvent.SKY_SAVED verb + to_prose branch: "X beholds the skyscape of {poss} birth, which yields {obj} a unique {Cap} capacity."; tied highest scores switch "a unique" → "equal", join w. "and" (2-way) or Oxford comma (3+), and pluralize "capacity" → "capacities"; pronouns resolved from actor.pronouns at render time, same machinery as SIG_READY/ROLE_SELECTED
- epic.utils.ELEMENT_CAPACITOR_NAMES + ELEMENT_ORDER + top_capacitors(elements) helper: maps Fire→Ardor Stone→Ossum Time→Tempo Space→Nexus Air→Pneuma Water→Humor; tolerates both flat-int and enriched-dict (`{count, contributors}`) chart_data shapes; returns capacitor names tied for highest count, ordered by canonical wheel ring
- epic.natus_save: on action=confirm, records GameEvent.SKY_SAVED w. top_capacitors=[…] before _notify_sky_confirmed; per-room billscroll AND billboard Most Recent Scroll pick up the new prose
- _natus_overlay.html _onSkyConfirmed: removed sea-partial fetch+inject; now calls closeNatus() + window.location.reload() so the gamer lands on the table hex w. the PICK SKY → PICK SEA btn swap (server-side, driven by sky_confirmed=True), then opts into the sea overlay manually. The auto-launch via 39e12d6 was buried by FTs that were pinning the wrong contract — gamer never had a chance to witness PICK SEA on the hex
- test_room_sea_select.py: three FTs renamed/rewired from auto-launch assertions (sea_overlay_appears_without_page_refresh, natus_overlay_not_visible_after_sky_confirm, sea_open_class_on_html_after_confirm) to (pick_sea_btn_visible_after_sky_confirm, natus_overlay_closed_after_sky_confirm, clicking_pick_sea_btn_opens_sea_overlay) — sea overlay now requires explicit PICK SEA click
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 01:57:35 -04:00
|
|
|
def test_pick_sea_btn_visible_after_sky_confirm(self):
|
btn-primary label renames + stage-card polarity color refinements — two interleaved threads from one session, committing together since both touch sig + sea stage cards ; LABEL RENAMES: PICK SIGS → SCAN SIGS (room.html #id_pick_sigs_btn), PICK SKY → CAST SKY (room.html #id_pick_sky_btn × 2), PICK SEA → DRAW SEA (room.html #id_pick_sea_btn), TAKE SIG → SAVE SIG (sig-select.js _takeSigBtn.textContent × 2 callsites + section comment) — Element IDs (id_pick_sky_btn etc.), URL names (epic:pick_sigs, epic:pick_sky), and Python state enums (TableStatus.PICK_SKY, PICK_SEA, SIG_SELECT) intentionally retained as stable identifiers; the renamed text is purely the .btn-primary user-facing label ; FT + IT mentions of the old labels swept in test_game_room_select_{sig,sky,sea,role}.py, test_billboard.py, setup_sea_session.py mgmt cmd, apps/epic/{views,utils,models,tasks,tests/integrated/test_views}.py, SigSelectSpec.js, sky_overlay/sea_overlay/dashboard/sky.html, _card-deck.scss, _sky.scss — all docstring/comment references updated for cascade-grep cleanliness ; STAGE-CARD COLOR + CLASS REFINEMENTS (earlier in session): sig-stage card text colour split per polarity — gravity gets --terUser on .fan-card-name + .fan-card-reversal-{name,qualifier} + .sig-qualifier-{above,below}, levity gets --quiUser on the same five slots; all selectors prefixed w. .sig-stage-card to match the 0,4,0 specificity of the default `.sig-stage .sig-stage-card .fan-card-face .sig-qualifier-*` rule (without the prefix the polarity overrides lose the cascade — .sig-qualifier-below was visibly stuck on the default --quiUser) ; .stat-face-label gets polarity-inverse colours — gravity stat-block bg is --secUser (opposite of card's --priUser) so the label takes --quiUser to stay legible; levity is the symmetric flip (label = --terUser on --priUser stat-block bg) ; levity card title/qualifier drop-shadow swapped from rgba(0,0,0,…) → rgba(255,255,255,…) — dark drop reads as harsh smudge against the inverted-frame levity --secUser bg; applied to both sig-overlay[data-polarity="levity"] stage card AND sea-stage--levity via $_sea-title-shadow-levity (former shared $_sea-title-shadow split into per-polarity {levity,gravity} variants) ; reversal-face class/content alignment so each `.fan-card-reversal-*` class always carries its semantic content — DOM order per arcana type controls visual layout after the 180° SPIN (DOM-second appears visually on top): Major → title in .fan-card-reversal-name @ DOM-second (visually top after spin), qualifier in .fan-card-reversal-qualifier @ DOM-first; Non-major → title in .fan-card-reversal-name @ DOM-first (visually bottom after spin), qualifier in .fan-card-reversal-qualifier @ DOM-second (preserves the original "qualifier word reads first after spin" layout for Middle/Minor arcana — e.g. "Relieving / Eight of Crowns" not "Eight of Crowns / Relieving") ; _tarot_fan.html renders per-arcana DOM order directly (Django template branches handle both layouts); sig + sea overlays render a fixed two-`<p>` skeleton (one DOM order) so stage-card.js's populator dynamically rewrites the two `<p>`s' className per arcana — Major/override branch flips DOM-second to .fan-card-reversal-name + content, DOM-first to .fan-card-reversal-qualifier; non-major branch keeps DOM-first as .fan-card-reversal-name + title, DOM-second as .fan-card-reversal-qualifier + reversalQualifier-or-polarity-fallback ; SigSelectSpec.js + SeaDealSpec.js fixtures + Major reversed-face assertion updated for the new semantic — TDD
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 00:25:10 -04:00
|
|
|
"""Confirming sky reloads the room to the hex w. DRAW SEA replacing
|
|
|
|
|
CAST SKY; the sea overlay is NOT auto-opened."""
|
2026-04-28 22:16:38 -04:00
|
|
|
self.create_pre_authenticated_session("founder@test.io")
|
|
|
|
|
self.browser.get(self.room_url)
|
|
|
|
|
|
btn-primary label renames + stage-card polarity color refinements — two interleaved threads from one session, committing together since both touch sig + sea stage cards ; LABEL RENAMES: PICK SIGS → SCAN SIGS (room.html #id_pick_sigs_btn), PICK SKY → CAST SKY (room.html #id_pick_sky_btn × 2), PICK SEA → DRAW SEA (room.html #id_pick_sea_btn), TAKE SIG → SAVE SIG (sig-select.js _takeSigBtn.textContent × 2 callsites + section comment) — Element IDs (id_pick_sky_btn etc.), URL names (epic:pick_sigs, epic:pick_sky), and Python state enums (TableStatus.PICK_SKY, PICK_SEA, SIG_SELECT) intentionally retained as stable identifiers; the renamed text is purely the .btn-primary user-facing label ; FT + IT mentions of the old labels swept in test_game_room_select_{sig,sky,sea,role}.py, test_billboard.py, setup_sea_session.py mgmt cmd, apps/epic/{views,utils,models,tasks,tests/integrated/test_views}.py, SigSelectSpec.js, sky_overlay/sea_overlay/dashboard/sky.html, _card-deck.scss, _sky.scss — all docstring/comment references updated for cascade-grep cleanliness ; STAGE-CARD COLOR + CLASS REFINEMENTS (earlier in session): sig-stage card text colour split per polarity — gravity gets --terUser on .fan-card-name + .fan-card-reversal-{name,qualifier} + .sig-qualifier-{above,below}, levity gets --quiUser on the same five slots; all selectors prefixed w. .sig-stage-card to match the 0,4,0 specificity of the default `.sig-stage .sig-stage-card .fan-card-face .sig-qualifier-*` rule (without the prefix the polarity overrides lose the cascade — .sig-qualifier-below was visibly stuck on the default --quiUser) ; .stat-face-label gets polarity-inverse colours — gravity stat-block bg is --secUser (opposite of card's --priUser) so the label takes --quiUser to stay legible; levity is the symmetric flip (label = --terUser on --priUser stat-block bg) ; levity card title/qualifier drop-shadow swapped from rgba(0,0,0,…) → rgba(255,255,255,…) — dark drop reads as harsh smudge against the inverted-frame levity --secUser bg; applied to both sig-overlay[data-polarity="levity"] stage card AND sea-stage--levity via $_sea-title-shadow-levity (former shared $_sea-title-shadow split into per-polarity {levity,gravity} variants) ; reversal-face class/content alignment so each `.fan-card-reversal-*` class always carries its semantic content — DOM order per arcana type controls visual layout after the 180° SPIN (DOM-second appears visually on top): Major → title in .fan-card-reversal-name @ DOM-second (visually top after spin), qualifier in .fan-card-reversal-qualifier @ DOM-first; Non-major → title in .fan-card-reversal-name @ DOM-first (visually bottom after spin), qualifier in .fan-card-reversal-qualifier @ DOM-second (preserves the original "qualifier word reads first after spin" layout for Middle/Minor arcana — e.g. "Relieving / Eight of Crowns" not "Eight of Crowns / Relieving") ; _tarot_fan.html renders per-arcana DOM order directly (Django template branches handle both layouts); sig + sea overlays render a fixed two-`<p>` skeleton (one DOM order) so stage-card.js's populator dynamically rewrites the two `<p>`s' className per arcana — Major/override branch flips DOM-second to .fan-card-reversal-name + content, DOM-first to .fan-card-reversal-qualifier; non-major branch keeps DOM-first as .fan-card-reversal-name + title, DOM-second as .fan-card-reversal-qualifier + reversalQualifier-or-polarity-fallback ; SigSelectSpec.js + SeaDealSpec.js fixtures + Major reversed-face assertion updated for the new semantic — TDD
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 00:25:10 -04:00
|
|
|
# Sky not yet confirmed — CAST SKY btn present.
|
2026-04-28 22:16:38 -04:00
|
|
|
self.wait_for(lambda: self.browser.find_element(By.ID, "id_pick_sky_btn"))
|
|
|
|
|
|
|
|
|
|
self._confirm_sky()
|
|
|
|
|
|
btn-primary label renames + stage-card polarity color refinements — two interleaved threads from one session, committing together since both touch sig + sea stage cards ; LABEL RENAMES: PICK SIGS → SCAN SIGS (room.html #id_pick_sigs_btn), PICK SKY → CAST SKY (room.html #id_pick_sky_btn × 2), PICK SEA → DRAW SEA (room.html #id_pick_sea_btn), TAKE SIG → SAVE SIG (sig-select.js _takeSigBtn.textContent × 2 callsites + section comment) — Element IDs (id_pick_sky_btn etc.), URL names (epic:pick_sigs, epic:pick_sky), and Python state enums (TableStatus.PICK_SKY, PICK_SEA, SIG_SELECT) intentionally retained as stable identifiers; the renamed text is purely the .btn-primary user-facing label ; FT + IT mentions of the old labels swept in test_game_room_select_{sig,sky,sea,role}.py, test_billboard.py, setup_sea_session.py mgmt cmd, apps/epic/{views,utils,models,tasks,tests/integrated/test_views}.py, SigSelectSpec.js, sky_overlay/sea_overlay/dashboard/sky.html, _card-deck.scss, _sky.scss — all docstring/comment references updated for cascade-grep cleanliness ; STAGE-CARD COLOR + CLASS REFINEMENTS (earlier in session): sig-stage card text colour split per polarity — gravity gets --terUser on .fan-card-name + .fan-card-reversal-{name,qualifier} + .sig-qualifier-{above,below}, levity gets --quiUser on the same five slots; all selectors prefixed w. .sig-stage-card to match the 0,4,0 specificity of the default `.sig-stage .sig-stage-card .fan-card-face .sig-qualifier-*` rule (without the prefix the polarity overrides lose the cascade — .sig-qualifier-below was visibly stuck on the default --quiUser) ; .stat-face-label gets polarity-inverse colours — gravity stat-block bg is --secUser (opposite of card's --priUser) so the label takes --quiUser to stay legible; levity is the symmetric flip (label = --terUser on --priUser stat-block bg) ; levity card title/qualifier drop-shadow swapped from rgba(0,0,0,…) → rgba(255,255,255,…) — dark drop reads as harsh smudge against the inverted-frame levity --secUser bg; applied to both sig-overlay[data-polarity="levity"] stage card AND sea-stage--levity via $_sea-title-shadow-levity (former shared $_sea-title-shadow split into per-polarity {levity,gravity} variants) ; reversal-face class/content alignment so each `.fan-card-reversal-*` class always carries its semantic content — DOM order per arcana type controls visual layout after the 180° SPIN (DOM-second appears visually on top): Major → title in .fan-card-reversal-name @ DOM-second (visually top after spin), qualifier in .fan-card-reversal-qualifier @ DOM-first; Non-major → title in .fan-card-reversal-name @ DOM-first (visually bottom after spin), qualifier in .fan-card-reversal-qualifier @ DOM-second (preserves the original "qualifier word reads first after spin" layout for Middle/Minor arcana — e.g. "Relieving / Eight of Crowns" not "Eight of Crowns / Relieving") ; _tarot_fan.html renders per-arcana DOM order directly (Django template branches handle both layouts); sig + sea overlays render a fixed two-`<p>` skeleton (one DOM order) so stage-card.js's populator dynamically rewrites the two `<p>`s' className per arcana — Major/override branch flips DOM-second to .fan-card-reversal-name + content, DOM-first to .fan-card-reversal-qualifier; non-major branch keeps DOM-first as .fan-card-reversal-name + title, DOM-second as .fan-card-reversal-qualifier + reversalQualifier-or-polarity-fallback ; SigSelectSpec.js + SeaDealSpec.js fixtures + Major reversed-face assertion updated for the new semantic — TDD
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 00:25:10 -04:00
|
|
|
# Page reloads → hex shows DRAW SEA in place of CAST SKY.
|
SAVE SKY provenance + sky→hex (not sky→sea) transition — TDD
- drama.GameEvent.SKY_SAVED verb + to_prose branch: "X beholds the skyscape of {poss} birth, which yields {obj} a unique {Cap} capacity."; tied highest scores switch "a unique" → "equal", join w. "and" (2-way) or Oxford comma (3+), and pluralize "capacity" → "capacities"; pronouns resolved from actor.pronouns at render time, same machinery as SIG_READY/ROLE_SELECTED
- epic.utils.ELEMENT_CAPACITOR_NAMES + ELEMENT_ORDER + top_capacitors(elements) helper: maps Fire→Ardor Stone→Ossum Time→Tempo Space→Nexus Air→Pneuma Water→Humor; tolerates both flat-int and enriched-dict (`{count, contributors}`) chart_data shapes; returns capacitor names tied for highest count, ordered by canonical wheel ring
- epic.natus_save: on action=confirm, records GameEvent.SKY_SAVED w. top_capacitors=[…] before _notify_sky_confirmed; per-room billscroll AND billboard Most Recent Scroll pick up the new prose
- _natus_overlay.html _onSkyConfirmed: removed sea-partial fetch+inject; now calls closeNatus() + window.location.reload() so the gamer lands on the table hex w. the PICK SKY → PICK SEA btn swap (server-side, driven by sky_confirmed=True), then opts into the sea overlay manually. The auto-launch via 39e12d6 was buried by FTs that were pinning the wrong contract — gamer never had a chance to witness PICK SEA on the hex
- test_room_sea_select.py: three FTs renamed/rewired from auto-launch assertions (sea_overlay_appears_without_page_refresh, natus_overlay_not_visible_after_sky_confirm, sea_open_class_on_html_after_confirm) to (pick_sea_btn_visible_after_sky_confirm, natus_overlay_closed_after_sky_confirm, clicking_pick_sea_btn_opens_sea_overlay) — sea overlay now requires explicit PICK SEA click
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 01:57:35 -04:00
|
|
|
self.wait_for(lambda: self.browser.find_element(By.ID, "id_pick_sea_btn"))
|
|
|
|
|
self.assertEqual(self.browser.find_elements(By.ID, "id_pick_sky_btn"), [])
|
|
|
|
|
|
|
|
|
|
# Sea overlay is NOT auto-opened — it only appears once the gamer
|
btn-primary label renames + stage-card polarity color refinements — two interleaved threads from one session, committing together since both touch sig + sea stage cards ; LABEL RENAMES: PICK SIGS → SCAN SIGS (room.html #id_pick_sigs_btn), PICK SKY → CAST SKY (room.html #id_pick_sky_btn × 2), PICK SEA → DRAW SEA (room.html #id_pick_sea_btn), TAKE SIG → SAVE SIG (sig-select.js _takeSigBtn.textContent × 2 callsites + section comment) — Element IDs (id_pick_sky_btn etc.), URL names (epic:pick_sigs, epic:pick_sky), and Python state enums (TableStatus.PICK_SKY, PICK_SEA, SIG_SELECT) intentionally retained as stable identifiers; the renamed text is purely the .btn-primary user-facing label ; FT + IT mentions of the old labels swept in test_game_room_select_{sig,sky,sea,role}.py, test_billboard.py, setup_sea_session.py mgmt cmd, apps/epic/{views,utils,models,tasks,tests/integrated/test_views}.py, SigSelectSpec.js, sky_overlay/sea_overlay/dashboard/sky.html, _card-deck.scss, _sky.scss — all docstring/comment references updated for cascade-grep cleanliness ; STAGE-CARD COLOR + CLASS REFINEMENTS (earlier in session): sig-stage card text colour split per polarity — gravity gets --terUser on .fan-card-name + .fan-card-reversal-{name,qualifier} + .sig-qualifier-{above,below}, levity gets --quiUser on the same five slots; all selectors prefixed w. .sig-stage-card to match the 0,4,0 specificity of the default `.sig-stage .sig-stage-card .fan-card-face .sig-qualifier-*` rule (without the prefix the polarity overrides lose the cascade — .sig-qualifier-below was visibly stuck on the default --quiUser) ; .stat-face-label gets polarity-inverse colours — gravity stat-block bg is --secUser (opposite of card's --priUser) so the label takes --quiUser to stay legible; levity is the symmetric flip (label = --terUser on --priUser stat-block bg) ; levity card title/qualifier drop-shadow swapped from rgba(0,0,0,…) → rgba(255,255,255,…) — dark drop reads as harsh smudge against the inverted-frame levity --secUser bg; applied to both sig-overlay[data-polarity="levity"] stage card AND sea-stage--levity via $_sea-title-shadow-levity (former shared $_sea-title-shadow split into per-polarity {levity,gravity} variants) ; reversal-face class/content alignment so each `.fan-card-reversal-*` class always carries its semantic content — DOM order per arcana type controls visual layout after the 180° SPIN (DOM-second appears visually on top): Major → title in .fan-card-reversal-name @ DOM-second (visually top after spin), qualifier in .fan-card-reversal-qualifier @ DOM-first; Non-major → title in .fan-card-reversal-name @ DOM-first (visually bottom after spin), qualifier in .fan-card-reversal-qualifier @ DOM-second (preserves the original "qualifier word reads first after spin" layout for Middle/Minor arcana — e.g. "Relieving / Eight of Crowns" not "Eight of Crowns / Relieving") ; _tarot_fan.html renders per-arcana DOM order directly (Django template branches handle both layouts); sig + sea overlays render a fixed two-`<p>` skeleton (one DOM order) so stage-card.js's populator dynamically rewrites the two `<p>`s' className per arcana — Major/override branch flips DOM-second to .fan-card-reversal-name + content, DOM-first to .fan-card-reversal-qualifier; non-major branch keeps DOM-first as .fan-card-reversal-name + title, DOM-second as .fan-card-reversal-qualifier + reversalQualifier-or-polarity-fallback ; SigSelectSpec.js + SeaDealSpec.js fixtures + Major reversed-face assertion updated for the new semantic — TDD
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 00:25:10 -04:00
|
|
|
# clicks DRAW SEA.
|
SAVE SKY provenance + sky→hex (not sky→sea) transition — TDD
- drama.GameEvent.SKY_SAVED verb + to_prose branch: "X beholds the skyscape of {poss} birth, which yields {obj} a unique {Cap} capacity."; tied highest scores switch "a unique" → "equal", join w. "and" (2-way) or Oxford comma (3+), and pluralize "capacity" → "capacities"; pronouns resolved from actor.pronouns at render time, same machinery as SIG_READY/ROLE_SELECTED
- epic.utils.ELEMENT_CAPACITOR_NAMES + ELEMENT_ORDER + top_capacitors(elements) helper: maps Fire→Ardor Stone→Ossum Time→Tempo Space→Nexus Air→Pneuma Water→Humor; tolerates both flat-int and enriched-dict (`{count, contributors}`) chart_data shapes; returns capacitor names tied for highest count, ordered by canonical wheel ring
- epic.natus_save: on action=confirm, records GameEvent.SKY_SAVED w. top_capacitors=[…] before _notify_sky_confirmed; per-room billscroll AND billboard Most Recent Scroll pick up the new prose
- _natus_overlay.html _onSkyConfirmed: removed sea-partial fetch+inject; now calls closeNatus() + window.location.reload() so the gamer lands on the table hex w. the PICK SKY → PICK SEA btn swap (server-side, driven by sky_confirmed=True), then opts into the sea overlay manually. The auto-launch via 39e12d6 was buried by FTs that were pinning the wrong contract — gamer never had a chance to witness PICK SEA on the hex
- test_room_sea_select.py: three FTs renamed/rewired from auto-launch assertions (sea_overlay_appears_without_page_refresh, natus_overlay_not_visible_after_sky_confirm, sea_open_class_on_html_after_confirm) to (pick_sea_btn_visible_after_sky_confirm, natus_overlay_closed_after_sky_confirm, clicking_pick_sea_btn_opens_sea_overlay) — sea overlay now requires explicit PICK SEA click
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 01:57:35 -04:00
|
|
|
has_sea_open = self.browser.execute_script(
|
|
|
|
|
"return document.documentElement.classList.contains('sea-open');"
|
2026-04-28 22:16:38 -04:00
|
|
|
)
|
SAVE SKY provenance + sky→hex (not sky→sea) transition — TDD
- drama.GameEvent.SKY_SAVED verb + to_prose branch: "X beholds the skyscape of {poss} birth, which yields {obj} a unique {Cap} capacity."; tied highest scores switch "a unique" → "equal", join w. "and" (2-way) or Oxford comma (3+), and pluralize "capacity" → "capacities"; pronouns resolved from actor.pronouns at render time, same machinery as SIG_READY/ROLE_SELECTED
- epic.utils.ELEMENT_CAPACITOR_NAMES + ELEMENT_ORDER + top_capacitors(elements) helper: maps Fire→Ardor Stone→Ossum Time→Tempo Space→Nexus Air→Pneuma Water→Humor; tolerates both flat-int and enriched-dict (`{count, contributors}`) chart_data shapes; returns capacitor names tied for highest count, ordered by canonical wheel ring
- epic.natus_save: on action=confirm, records GameEvent.SKY_SAVED w. top_capacitors=[…] before _notify_sky_confirmed; per-room billscroll AND billboard Most Recent Scroll pick up the new prose
- _natus_overlay.html _onSkyConfirmed: removed sea-partial fetch+inject; now calls closeNatus() + window.location.reload() so the gamer lands on the table hex w. the PICK SKY → PICK SEA btn swap (server-side, driven by sky_confirmed=True), then opts into the sea overlay manually. The auto-launch via 39e12d6 was buried by FTs that were pinning the wrong contract — gamer never had a chance to witness PICK SEA on the hex
- test_room_sea_select.py: three FTs renamed/rewired from auto-launch assertions (sea_overlay_appears_without_page_refresh, natus_overlay_not_visible_after_sky_confirm, sea_open_class_on_html_after_confirm) to (pick_sea_btn_visible_after_sky_confirm, natus_overlay_closed_after_sky_confirm, clicking_pick_sea_btn_opens_sea_overlay) — sea overlay now requires explicit PICK SEA click
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 01:57:35 -04:00
|
|
|
self.assertFalse(has_sea_open)
|
2026-04-28 22:16:38 -04:00
|
|
|
|
rename natus → sky across the codebase — natal chart abstraction is now sky throughout, since chart inputs aren't birthday-gated
Mechanical rename: 5 files (sky-wheel.js, _sky.scss, _sky_overlay.html, SkyWheelSpec.js x2), 24 in-place edits across templates/views/urls/SCSS/JS/tests/CLAUDE.md. URL names epic:natus_save → epic:sky_save (epic namespaced, no clash w. dashboard:sky_save), JS module NatusWheel → SkyWheel, DOM ids id_natus_* → id_sky_*, BEM classes natus-* → sky-*, dashboard sky_natus_data/sky_natus_preview collapsed to sky_data/sky_preview_data. No DB migration needed (User.sky_chart_data + GameEvent.SKY_SAVED already used sky-prefix). 778 ITs + Jasmine green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 20:36:15 -04:00
|
|
|
def test_sky_overlay_closed_after_sky_confirm(self):
|
|
|
|
|
"""Sky overlay is gone (page reloaded) after sky confirm."""
|
2026-04-28 22:16:38 -04:00
|
|
|
self.create_pre_authenticated_session("founder@test.io")
|
|
|
|
|
self.browser.get(self.room_url)
|
|
|
|
|
self.wait_for(lambda: self.browser.find_element(By.ID, "id_pick_sky_btn"))
|
|
|
|
|
|
|
|
|
|
self._confirm_sky()
|
|
|
|
|
|
SAVE SKY provenance + sky→hex (not sky→sea) transition — TDD
- drama.GameEvent.SKY_SAVED verb + to_prose branch: "X beholds the skyscape of {poss} birth, which yields {obj} a unique {Cap} capacity."; tied highest scores switch "a unique" → "equal", join w. "and" (2-way) or Oxford comma (3+), and pluralize "capacity" → "capacities"; pronouns resolved from actor.pronouns at render time, same machinery as SIG_READY/ROLE_SELECTED
- epic.utils.ELEMENT_CAPACITOR_NAMES + ELEMENT_ORDER + top_capacitors(elements) helper: maps Fire→Ardor Stone→Ossum Time→Tempo Space→Nexus Air→Pneuma Water→Humor; tolerates both flat-int and enriched-dict (`{count, contributors}`) chart_data shapes; returns capacitor names tied for highest count, ordered by canonical wheel ring
- epic.natus_save: on action=confirm, records GameEvent.SKY_SAVED w. top_capacitors=[…] before _notify_sky_confirmed; per-room billscroll AND billboard Most Recent Scroll pick up the new prose
- _natus_overlay.html _onSkyConfirmed: removed sea-partial fetch+inject; now calls closeNatus() + window.location.reload() so the gamer lands on the table hex w. the PICK SKY → PICK SEA btn swap (server-side, driven by sky_confirmed=True), then opts into the sea overlay manually. The auto-launch via 39e12d6 was buried by FTs that were pinning the wrong contract — gamer never had a chance to witness PICK SEA on the hex
- test_room_sea_select.py: three FTs renamed/rewired from auto-launch assertions (sea_overlay_appears_without_page_refresh, natus_overlay_not_visible_after_sky_confirm, sea_open_class_on_html_after_confirm) to (pick_sea_btn_visible_after_sky_confirm, natus_overlay_closed_after_sky_confirm, clicking_pick_sea_btn_opens_sea_overlay) — sea overlay now requires explicit PICK SEA click
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 01:57:35 -04:00
|
|
|
self.wait_for(lambda: self.browser.find_element(By.ID, "id_pick_sea_btn"))
|
rename natus → sky across the codebase — natal chart abstraction is now sky throughout, since chart inputs aren't birthday-gated
Mechanical rename: 5 files (sky-wheel.js, _sky.scss, _sky_overlay.html, SkyWheelSpec.js x2), 24 in-place edits across templates/views/urls/SCSS/JS/tests/CLAUDE.md. URL names epic:natus_save → epic:sky_save (epic namespaced, no clash w. dashboard:sky_save), JS module NatusWheel → SkyWheel, DOM ids id_natus_* → id_sky_*, BEM classes natus-* → sky-*, dashboard sky_natus_data/sky_natus_preview collapsed to sky_data/sky_preview_data. No DB migration needed (User.sky_chart_data + GameEvent.SKY_SAVED already used sky-prefix). 778 ITs + Jasmine green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 20:36:15 -04:00
|
|
|
sky = self.browser.find_elements(By.ID, "id_sky_overlay")
|
|
|
|
|
self.assertTrue(not sky or not sky[0].is_displayed())
|
2026-04-28 22:16:38 -04:00
|
|
|
|
SAVE SKY provenance + sky→hex (not sky→sea) transition — TDD
- drama.GameEvent.SKY_SAVED verb + to_prose branch: "X beholds the skyscape of {poss} birth, which yields {obj} a unique {Cap} capacity."; tied highest scores switch "a unique" → "equal", join w. "and" (2-way) or Oxford comma (3+), and pluralize "capacity" → "capacities"; pronouns resolved from actor.pronouns at render time, same machinery as SIG_READY/ROLE_SELECTED
- epic.utils.ELEMENT_CAPACITOR_NAMES + ELEMENT_ORDER + top_capacitors(elements) helper: maps Fire→Ardor Stone→Ossum Time→Tempo Space→Nexus Air→Pneuma Water→Humor; tolerates both flat-int and enriched-dict (`{count, contributors}`) chart_data shapes; returns capacitor names tied for highest count, ordered by canonical wheel ring
- epic.natus_save: on action=confirm, records GameEvent.SKY_SAVED w. top_capacitors=[…] before _notify_sky_confirmed; per-room billscroll AND billboard Most Recent Scroll pick up the new prose
- _natus_overlay.html _onSkyConfirmed: removed sea-partial fetch+inject; now calls closeNatus() + window.location.reload() so the gamer lands on the table hex w. the PICK SKY → PICK SEA btn swap (server-side, driven by sky_confirmed=True), then opts into the sea overlay manually. The auto-launch via 39e12d6 was buried by FTs that were pinning the wrong contract — gamer never had a chance to witness PICK SEA on the hex
- test_room_sea_select.py: three FTs renamed/rewired from auto-launch assertions (sea_overlay_appears_without_page_refresh, natus_overlay_not_visible_after_sky_confirm, sea_open_class_on_html_after_confirm) to (pick_sea_btn_visible_after_sky_confirm, natus_overlay_closed_after_sky_confirm, clicking_pick_sea_btn_opens_sea_overlay) — sea overlay now requires explicit PICK SEA click
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 01:57:35 -04:00
|
|
|
def test_clicking_pick_sea_btn_opens_sea_overlay(self):
|
btn-primary label renames + stage-card polarity color refinements — two interleaved threads from one session, committing together since both touch sig + sea stage cards ; LABEL RENAMES: PICK SIGS → SCAN SIGS (room.html #id_pick_sigs_btn), PICK SKY → CAST SKY (room.html #id_pick_sky_btn × 2), PICK SEA → DRAW SEA (room.html #id_pick_sea_btn), TAKE SIG → SAVE SIG (sig-select.js _takeSigBtn.textContent × 2 callsites + section comment) — Element IDs (id_pick_sky_btn etc.), URL names (epic:pick_sigs, epic:pick_sky), and Python state enums (TableStatus.PICK_SKY, PICK_SEA, SIG_SELECT) intentionally retained as stable identifiers; the renamed text is purely the .btn-primary user-facing label ; FT + IT mentions of the old labels swept in test_game_room_select_{sig,sky,sea,role}.py, test_billboard.py, setup_sea_session.py mgmt cmd, apps/epic/{views,utils,models,tasks,tests/integrated/test_views}.py, SigSelectSpec.js, sky_overlay/sea_overlay/dashboard/sky.html, _card-deck.scss, _sky.scss — all docstring/comment references updated for cascade-grep cleanliness ; STAGE-CARD COLOR + CLASS REFINEMENTS (earlier in session): sig-stage card text colour split per polarity — gravity gets --terUser on .fan-card-name + .fan-card-reversal-{name,qualifier} + .sig-qualifier-{above,below}, levity gets --quiUser on the same five slots; all selectors prefixed w. .sig-stage-card to match the 0,4,0 specificity of the default `.sig-stage .sig-stage-card .fan-card-face .sig-qualifier-*` rule (without the prefix the polarity overrides lose the cascade — .sig-qualifier-below was visibly stuck on the default --quiUser) ; .stat-face-label gets polarity-inverse colours — gravity stat-block bg is --secUser (opposite of card's --priUser) so the label takes --quiUser to stay legible; levity is the symmetric flip (label = --terUser on --priUser stat-block bg) ; levity card title/qualifier drop-shadow swapped from rgba(0,0,0,…) → rgba(255,255,255,…) — dark drop reads as harsh smudge against the inverted-frame levity --secUser bg; applied to both sig-overlay[data-polarity="levity"] stage card AND sea-stage--levity via $_sea-title-shadow-levity (former shared $_sea-title-shadow split into per-polarity {levity,gravity} variants) ; reversal-face class/content alignment so each `.fan-card-reversal-*` class always carries its semantic content — DOM order per arcana type controls visual layout after the 180° SPIN (DOM-second appears visually on top): Major → title in .fan-card-reversal-name @ DOM-second (visually top after spin), qualifier in .fan-card-reversal-qualifier @ DOM-first; Non-major → title in .fan-card-reversal-name @ DOM-first (visually bottom after spin), qualifier in .fan-card-reversal-qualifier @ DOM-second (preserves the original "qualifier word reads first after spin" layout for Middle/Minor arcana — e.g. "Relieving / Eight of Crowns" not "Eight of Crowns / Relieving") ; _tarot_fan.html renders per-arcana DOM order directly (Django template branches handle both layouts); sig + sea overlays render a fixed two-`<p>` skeleton (one DOM order) so stage-card.js's populator dynamically rewrites the two `<p>`s' className per arcana — Major/override branch flips DOM-second to .fan-card-reversal-name + content, DOM-first to .fan-card-reversal-qualifier; non-major branch keeps DOM-first as .fan-card-reversal-name + title, DOM-second as .fan-card-reversal-qualifier + reversalQualifier-or-polarity-fallback ; SigSelectSpec.js + SeaDealSpec.js fixtures + Major reversed-face assertion updated for the new semantic — TDD
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 00:25:10 -04:00
|
|
|
"""The gamer's explicit click on DRAW SEA is what opens the sea overlay."""
|
2026-04-28 22:16:38 -04:00
|
|
|
self.create_pre_authenticated_session("founder@test.io")
|
|
|
|
|
self.browser.get(self.room_url)
|
|
|
|
|
self.wait_for(lambda: self.browser.find_element(By.ID, "id_pick_sky_btn"))
|
|
|
|
|
|
|
|
|
|
self._confirm_sky()
|
2026-05-04 02:23:47 -04:00
|
|
|
self.wait_for(lambda: self.browser.find_element(By.ID, "id_pick_sea_btn"))
|
2026-04-28 22:16:38 -04:00
|
|
|
|
btn-primary label renames + stage-card polarity color refinements — two interleaved threads from one session, committing together since both touch sig + sea stage cards ; LABEL RENAMES: PICK SIGS → SCAN SIGS (room.html #id_pick_sigs_btn), PICK SKY → CAST SKY (room.html #id_pick_sky_btn × 2), PICK SEA → DRAW SEA (room.html #id_pick_sea_btn), TAKE SIG → SAVE SIG (sig-select.js _takeSigBtn.textContent × 2 callsites + section comment) — Element IDs (id_pick_sky_btn etc.), URL names (epic:pick_sigs, epic:pick_sky), and Python state enums (TableStatus.PICK_SKY, PICK_SEA, SIG_SELECT) intentionally retained as stable identifiers; the renamed text is purely the .btn-primary user-facing label ; FT + IT mentions of the old labels swept in test_game_room_select_{sig,sky,sea,role}.py, test_billboard.py, setup_sea_session.py mgmt cmd, apps/epic/{views,utils,models,tasks,tests/integrated/test_views}.py, SigSelectSpec.js, sky_overlay/sea_overlay/dashboard/sky.html, _card-deck.scss, _sky.scss — all docstring/comment references updated for cascade-grep cleanliness ; STAGE-CARD COLOR + CLASS REFINEMENTS (earlier in session): sig-stage card text colour split per polarity — gravity gets --terUser on .fan-card-name + .fan-card-reversal-{name,qualifier} + .sig-qualifier-{above,below}, levity gets --quiUser on the same five slots; all selectors prefixed w. .sig-stage-card to match the 0,4,0 specificity of the default `.sig-stage .sig-stage-card .fan-card-face .sig-qualifier-*` rule (without the prefix the polarity overrides lose the cascade — .sig-qualifier-below was visibly stuck on the default --quiUser) ; .stat-face-label gets polarity-inverse colours — gravity stat-block bg is --secUser (opposite of card's --priUser) so the label takes --quiUser to stay legible; levity is the symmetric flip (label = --terUser on --priUser stat-block bg) ; levity card title/qualifier drop-shadow swapped from rgba(0,0,0,…) → rgba(255,255,255,…) — dark drop reads as harsh smudge against the inverted-frame levity --secUser bg; applied to both sig-overlay[data-polarity="levity"] stage card AND sea-stage--levity via $_sea-title-shadow-levity (former shared $_sea-title-shadow split into per-polarity {levity,gravity} variants) ; reversal-face class/content alignment so each `.fan-card-reversal-*` class always carries its semantic content — DOM order per arcana type controls visual layout after the 180° SPIN (DOM-second appears visually on top): Major → title in .fan-card-reversal-name @ DOM-second (visually top after spin), qualifier in .fan-card-reversal-qualifier @ DOM-first; Non-major → title in .fan-card-reversal-name @ DOM-first (visually bottom after spin), qualifier in .fan-card-reversal-qualifier @ DOM-second (preserves the original "qualifier word reads first after spin" layout for Middle/Minor arcana — e.g. "Relieving / Eight of Crowns" not "Eight of Crowns / Relieving") ; _tarot_fan.html renders per-arcana DOM order directly (Django template branches handle both layouts); sig + sea overlays render a fixed two-`<p>` skeleton (one DOM order) so stage-card.js's populator dynamically rewrites the two `<p>`s' className per arcana — Major/override branch flips DOM-second to .fan-card-reversal-name + content, DOM-first to .fan-card-reversal-qualifier; non-major branch keeps DOM-first as .fan-card-reversal-name + title, DOM-second as .fan-card-reversal-qualifier + reversalQualifier-or-polarity-fallback ; SigSelectSpec.js + SeaDealSpec.js fixtures + Major reversed-face assertion updated for the new semantic — TDD
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 00:25:10 -04:00
|
|
|
# On slow CI, the DRAW SEA btn parses into the DOM before the inline
|
2026-05-04 02:23:47 -04:00
|
|
|
# `<script>` at the bottom of _sea_overlay.html has bound `openSea` to
|
|
|
|
|
# it; a one-shot click can land before the handler exists. Retry click
|
|
|
|
|
# + assert together via wait_for so the race resolves naturally.
|
|
|
|
|
def _click_and_assert_sea_open():
|
|
|
|
|
btn = self.browser.find_element(By.ID, "id_pick_sea_btn")
|
|
|
|
|
self.browser.execute_script("arguments[0].click()", btn)
|
|
|
|
|
self.assertTrue(self.browser.execute_script(
|
|
|
|
|
"return document.documentElement.classList.contains('sea-open')"
|
|
|
|
|
))
|
|
|
|
|
self.wait_for(_click_and_assert_sea_open)
|
|
|
|
|
|
|
|
|
|
sea_overlay = self.browser.find_element(By.ID, "id_sea_overlay")
|
SAVE SKY provenance + sky→hex (not sky→sea) transition — TDD
- drama.GameEvent.SKY_SAVED verb + to_prose branch: "X beholds the skyscape of {poss} birth, which yields {obj} a unique {Cap} capacity."; tied highest scores switch "a unique" → "equal", join w. "and" (2-way) or Oxford comma (3+), and pluralize "capacity" → "capacities"; pronouns resolved from actor.pronouns at render time, same machinery as SIG_READY/ROLE_SELECTED
- epic.utils.ELEMENT_CAPACITOR_NAMES + ELEMENT_ORDER + top_capacitors(elements) helper: maps Fire→Ardor Stone→Ossum Time→Tempo Space→Nexus Air→Pneuma Water→Humor; tolerates both flat-int and enriched-dict (`{count, contributors}`) chart_data shapes; returns capacitor names tied for highest count, ordered by canonical wheel ring
- epic.natus_save: on action=confirm, records GameEvent.SKY_SAVED w. top_capacitors=[…] before _notify_sky_confirmed; per-room billscroll AND billboard Most Recent Scroll pick up the new prose
- _natus_overlay.html _onSkyConfirmed: removed sea-partial fetch+inject; now calls closeNatus() + window.location.reload() so the gamer lands on the table hex w. the PICK SKY → PICK SEA btn swap (server-side, driven by sky_confirmed=True), then opts into the sea overlay manually. The auto-launch via 39e12d6 was buried by FTs that were pinning the wrong contract — gamer never had a chance to witness PICK SEA on the hex
- test_room_sea_select.py: three FTs renamed/rewired from auto-launch assertions (sea_overlay_appears_without_page_refresh, natus_overlay_not_visible_after_sky_confirm, sea_open_class_on_html_after_confirm) to (pick_sea_btn_visible_after_sky_confirm, natus_overlay_closed_after_sky_confirm, clicking_pick_sea_btn_opens_sea_overlay) — sea overlay now requires explicit PICK SEA click
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 01:57:35 -04:00
|
|
|
self.assertTrue(sea_overlay.is_displayed())
|
2026-04-28 22:50:39 -04:00
|
|
|
|
|
|
|
|
|
btn-primary label renames + stage-card polarity color refinements — two interleaved threads from one session, committing together since both touch sig + sea stage cards ; LABEL RENAMES: PICK SIGS → SCAN SIGS (room.html #id_pick_sigs_btn), PICK SKY → CAST SKY (room.html #id_pick_sky_btn × 2), PICK SEA → DRAW SEA (room.html #id_pick_sea_btn), TAKE SIG → SAVE SIG (sig-select.js _takeSigBtn.textContent × 2 callsites + section comment) — Element IDs (id_pick_sky_btn etc.), URL names (epic:pick_sigs, epic:pick_sky), and Python state enums (TableStatus.PICK_SKY, PICK_SEA, SIG_SELECT) intentionally retained as stable identifiers; the renamed text is purely the .btn-primary user-facing label ; FT + IT mentions of the old labels swept in test_game_room_select_{sig,sky,sea,role}.py, test_billboard.py, setup_sea_session.py mgmt cmd, apps/epic/{views,utils,models,tasks,tests/integrated/test_views}.py, SigSelectSpec.js, sky_overlay/sea_overlay/dashboard/sky.html, _card-deck.scss, _sky.scss — all docstring/comment references updated for cascade-grep cleanliness ; STAGE-CARD COLOR + CLASS REFINEMENTS (earlier in session): sig-stage card text colour split per polarity — gravity gets --terUser on .fan-card-name + .fan-card-reversal-{name,qualifier} + .sig-qualifier-{above,below}, levity gets --quiUser on the same five slots; all selectors prefixed w. .sig-stage-card to match the 0,4,0 specificity of the default `.sig-stage .sig-stage-card .fan-card-face .sig-qualifier-*` rule (without the prefix the polarity overrides lose the cascade — .sig-qualifier-below was visibly stuck on the default --quiUser) ; .stat-face-label gets polarity-inverse colours — gravity stat-block bg is --secUser (opposite of card's --priUser) so the label takes --quiUser to stay legible; levity is the symmetric flip (label = --terUser on --priUser stat-block bg) ; levity card title/qualifier drop-shadow swapped from rgba(0,0,0,…) → rgba(255,255,255,…) — dark drop reads as harsh smudge against the inverted-frame levity --secUser bg; applied to both sig-overlay[data-polarity="levity"] stage card AND sea-stage--levity via $_sea-title-shadow-levity (former shared $_sea-title-shadow split into per-polarity {levity,gravity} variants) ; reversal-face class/content alignment so each `.fan-card-reversal-*` class always carries its semantic content — DOM order per arcana type controls visual layout after the 180° SPIN (DOM-second appears visually on top): Major → title in .fan-card-reversal-name @ DOM-second (visually top after spin), qualifier in .fan-card-reversal-qualifier @ DOM-first; Non-major → title in .fan-card-reversal-name @ DOM-first (visually bottom after spin), qualifier in .fan-card-reversal-qualifier @ DOM-second (preserves the original "qualifier word reads first after spin" layout for Middle/Minor arcana — e.g. "Relieving / Eight of Crowns" not "Eight of Crowns / Relieving") ; _tarot_fan.html renders per-arcana DOM order directly (Django template branches handle both layouts); sig + sea overlays render a fixed two-`<p>` skeleton (one DOM order) so stage-card.js's populator dynamically rewrites the two `<p>`s' className per arcana — Major/override branch flips DOM-second to .fan-card-reversal-name + content, DOM-first to .fan-card-reversal-qualifier; non-major branch keeps DOM-first as .fan-card-reversal-name + title, DOM-second as .fan-card-reversal-qualifier + reversalQualifier-or-polarity-fallback ; SigSelectSpec.js + SeaDealSpec.js fixtures + Major reversed-face assertion updated for the new semantic — TDD
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 00:25:10 -04:00
|
|
|
# ── Helpers for DRAW SEA deal tests ──────────────────────────────────────────
|
2026-04-28 22:50:39 -04:00
|
|
|
|
|
|
|
|
def _seed_earthman_cards(earthman, count=20):
|
|
|
|
|
"""Seed enough Middle Arcana cards for the deck piles."""
|
|
|
|
|
suits = ["BRANDS", "GRAILS", "BLADES", "CROWNS"]
|
|
|
|
|
nums = [11, 12, 13, 14]
|
|
|
|
|
for suit in suits:
|
|
|
|
|
for num in nums:
|
|
|
|
|
TarotCard.objects.get_or_create(
|
|
|
|
|
deck_variant=earthman,
|
|
|
|
|
slug=f"m{num}-{suit.lower()}-em",
|
|
|
|
|
defaults={"arcana": "MIDDLE", "suit": suit, "number": num,
|
|
|
|
|
"name": f"Card {num} {suit}"},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _make_sea_ready_room(earthman):
|
btn-primary label renames + stage-card polarity color refinements — two interleaved threads from one session, committing together since both touch sig + sea stage cards ; LABEL RENAMES: PICK SIGS → SCAN SIGS (room.html #id_pick_sigs_btn), PICK SKY → CAST SKY (room.html #id_pick_sky_btn × 2), PICK SEA → DRAW SEA (room.html #id_pick_sea_btn), TAKE SIG → SAVE SIG (sig-select.js _takeSigBtn.textContent × 2 callsites + section comment) — Element IDs (id_pick_sky_btn etc.), URL names (epic:pick_sigs, epic:pick_sky), and Python state enums (TableStatus.PICK_SKY, PICK_SEA, SIG_SELECT) intentionally retained as stable identifiers; the renamed text is purely the .btn-primary user-facing label ; FT + IT mentions of the old labels swept in test_game_room_select_{sig,sky,sea,role}.py, test_billboard.py, setup_sea_session.py mgmt cmd, apps/epic/{views,utils,models,tasks,tests/integrated/test_views}.py, SigSelectSpec.js, sky_overlay/sea_overlay/dashboard/sky.html, _card-deck.scss, _sky.scss — all docstring/comment references updated for cascade-grep cleanliness ; STAGE-CARD COLOR + CLASS REFINEMENTS (earlier in session): sig-stage card text colour split per polarity — gravity gets --terUser on .fan-card-name + .fan-card-reversal-{name,qualifier} + .sig-qualifier-{above,below}, levity gets --quiUser on the same five slots; all selectors prefixed w. .sig-stage-card to match the 0,4,0 specificity of the default `.sig-stage .sig-stage-card .fan-card-face .sig-qualifier-*` rule (without the prefix the polarity overrides lose the cascade — .sig-qualifier-below was visibly stuck on the default --quiUser) ; .stat-face-label gets polarity-inverse colours — gravity stat-block bg is --secUser (opposite of card's --priUser) so the label takes --quiUser to stay legible; levity is the symmetric flip (label = --terUser on --priUser stat-block bg) ; levity card title/qualifier drop-shadow swapped from rgba(0,0,0,…) → rgba(255,255,255,…) — dark drop reads as harsh smudge against the inverted-frame levity --secUser bg; applied to both sig-overlay[data-polarity="levity"] stage card AND sea-stage--levity via $_sea-title-shadow-levity (former shared $_sea-title-shadow split into per-polarity {levity,gravity} variants) ; reversal-face class/content alignment so each `.fan-card-reversal-*` class always carries its semantic content — DOM order per arcana type controls visual layout after the 180° SPIN (DOM-second appears visually on top): Major → title in .fan-card-reversal-name @ DOM-second (visually top after spin), qualifier in .fan-card-reversal-qualifier @ DOM-first; Non-major → title in .fan-card-reversal-name @ DOM-first (visually bottom after spin), qualifier in .fan-card-reversal-qualifier @ DOM-second (preserves the original "qualifier word reads first after spin" layout for Middle/Minor arcana — e.g. "Relieving / Eight of Crowns" not "Eight of Crowns / Relieving") ; _tarot_fan.html renders per-arcana DOM order directly (Django template branches handle both layouts); sig + sea overlays render a fixed two-`<p>` skeleton (one DOM order) so stage-card.js's populator dynamically rewrites the two `<p>`s' className per arcana — Major/override branch flips DOM-second to .fan-card-reversal-name + content, DOM-first to .fan-card-reversal-qualifier; non-major branch keeps DOM-first as .fan-card-reversal-name + title, DOM-second as .fan-card-reversal-qualifier + reversalQualifier-or-polarity-fallback ; SigSelectSpec.js + SeaDealSpec.js fixtures + Major reversed-face assertion updated for the new semantic — TDD
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 00:25:10 -04:00
|
|
|
"""Create a SKY_SELECT room with a confirmed Character ready for DRAW SEA.
|
2026-04-28 22:50:39 -04:00
|
|
|
|
|
|
|
|
Returns (room, gamer, seat, char, room_url).
|
|
|
|
|
"""
|
|
|
|
|
gamer, _ = User.objects.get_or_create(email="founder@test.io")
|
|
|
|
|
gamer.unlocked_decks.add(earthman)
|
|
|
|
|
gamer.equipped_deck = earthman
|
|
|
|
|
gamer.save(update_fields=["equipped_deck"])
|
|
|
|
|
|
|
|
|
|
room = Room.objects.create(
|
|
|
|
|
name="Sea Deal Room", table_status=Room.SKY_SELECT, owner=gamer
|
|
|
|
|
)
|
|
|
|
|
slot = room.gate_slots.get(slot_number=1)
|
|
|
|
|
slot.gamer = gamer
|
|
|
|
|
slot.status = GateSlot.FILLED
|
|
|
|
|
slot.save()
|
|
|
|
|
room.gate_status = Room.OPEN
|
|
|
|
|
room.save()
|
|
|
|
|
|
|
|
|
|
sig_card = TarotCard.objects.filter(deck_variant=earthman, arcana="MAJOR").first()
|
|
|
|
|
seat = TableSeat.objects.create(
|
|
|
|
|
room=room, gamer=gamer, role="PC", slot_number=1,
|
|
|
|
|
deck_variant=earthman, significator=sig_card,
|
|
|
|
|
)
|
|
|
|
|
char = Character.objects.create(
|
|
|
|
|
seat=seat, significator=sig_card, confirmed_at=timezone.now()
|
|
|
|
|
)
|
|
|
|
|
return room, gamer, seat, char
|
|
|
|
|
|
|
|
|
|
|
2026-04-29 11:27:51 -04:00
|
|
|
@tag("channels")
|
2026-04-28 22:50:39 -04:00
|
|
|
class PickSeaDealTest(ChannelsFunctionalTest):
|
btn-primary label renames + stage-card polarity color refinements — two interleaved threads from one session, committing together since both touch sig + sea stage cards ; LABEL RENAMES: PICK SIGS → SCAN SIGS (room.html #id_pick_sigs_btn), PICK SKY → CAST SKY (room.html #id_pick_sky_btn × 2), PICK SEA → DRAW SEA (room.html #id_pick_sea_btn), TAKE SIG → SAVE SIG (sig-select.js _takeSigBtn.textContent × 2 callsites + section comment) — Element IDs (id_pick_sky_btn etc.), URL names (epic:pick_sigs, epic:pick_sky), and Python state enums (TableStatus.PICK_SKY, PICK_SEA, SIG_SELECT) intentionally retained as stable identifiers; the renamed text is purely the .btn-primary user-facing label ; FT + IT mentions of the old labels swept in test_game_room_select_{sig,sky,sea,role}.py, test_billboard.py, setup_sea_session.py mgmt cmd, apps/epic/{views,utils,models,tasks,tests/integrated/test_views}.py, SigSelectSpec.js, sky_overlay/sea_overlay/dashboard/sky.html, _card-deck.scss, _sky.scss — all docstring/comment references updated for cascade-grep cleanliness ; STAGE-CARD COLOR + CLASS REFINEMENTS (earlier in session): sig-stage card text colour split per polarity — gravity gets --terUser on .fan-card-name + .fan-card-reversal-{name,qualifier} + .sig-qualifier-{above,below}, levity gets --quiUser on the same five slots; all selectors prefixed w. .sig-stage-card to match the 0,4,0 specificity of the default `.sig-stage .sig-stage-card .fan-card-face .sig-qualifier-*` rule (without the prefix the polarity overrides lose the cascade — .sig-qualifier-below was visibly stuck on the default --quiUser) ; .stat-face-label gets polarity-inverse colours — gravity stat-block bg is --secUser (opposite of card's --priUser) so the label takes --quiUser to stay legible; levity is the symmetric flip (label = --terUser on --priUser stat-block bg) ; levity card title/qualifier drop-shadow swapped from rgba(0,0,0,…) → rgba(255,255,255,…) — dark drop reads as harsh smudge against the inverted-frame levity --secUser bg; applied to both sig-overlay[data-polarity="levity"] stage card AND sea-stage--levity via $_sea-title-shadow-levity (former shared $_sea-title-shadow split into per-polarity {levity,gravity} variants) ; reversal-face class/content alignment so each `.fan-card-reversal-*` class always carries its semantic content — DOM order per arcana type controls visual layout after the 180° SPIN (DOM-second appears visually on top): Major → title in .fan-card-reversal-name @ DOM-second (visually top after spin), qualifier in .fan-card-reversal-qualifier @ DOM-first; Non-major → title in .fan-card-reversal-name @ DOM-first (visually bottom after spin), qualifier in .fan-card-reversal-qualifier @ DOM-second (preserves the original "qualifier word reads first after spin" layout for Middle/Minor arcana — e.g. "Relieving / Eight of Crowns" not "Eight of Crowns / Relieving") ; _tarot_fan.html renders per-arcana DOM order directly (Django template branches handle both layouts); sig + sea overlays render a fixed two-`<p>` skeleton (one DOM order) so stage-card.js's populator dynamically rewrites the two `<p>`s' className per arcana — Major/override branch flips DOM-second to .fan-card-reversal-name + content, DOM-first to .fan-card-reversal-qualifier; non-major branch keeps DOM-first as .fan-card-reversal-name + title, DOM-second as .fan-card-reversal-qualifier + reversalQualifier-or-polarity-fallback ; SigSelectSpec.js + SeaDealSpec.js fixtures + Major reversed-face assertion updated for the new semantic — TDD
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 00:25:10 -04:00
|
|
|
"""DRAW SEA deck stacks, OK btn interaction, card draw, and LOCK HAND."""
|
2026-04-28 22:50:39 -04:00
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
super().setUp()
|
|
|
|
|
self.browser.set_window_size(800, 1200)
|
|
|
|
|
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"}
|
|
|
|
|
)
|
|
|
|
|
# Major Arcana sig card
|
|
|
|
|
earthman, _ = DeckVariant.objects.get_or_create(
|
|
|
|
|
slug="earthman",
|
|
|
|
|
defaults={"name": "Earthman Deck", "card_count": 108, "is_default": True},
|
|
|
|
|
)
|
|
|
|
|
TarotCard.objects.get_or_create(
|
|
|
|
|
deck_variant=earthman, slug="the-schizo-em",
|
|
|
|
|
defaults={"arcana": "MAJOR", "number": 1, "name": "The Schizo",
|
|
|
|
|
"levity_qualifier": "Enlightened", "gravity_qualifier": "Engraven"},
|
|
|
|
|
)
|
|
|
|
|
_seed_earthman_cards(earthman)
|
|
|
|
|
self.room, self.gamer, self.seat, self.char = _make_sea_ready_room(earthman)
|
|
|
|
|
self.room_url = self.live_server_url + reverse(
|
|
|
|
|
"epic:room", kwargs={"room_id": self.room.id}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def _load_sea_overlay(self):
|
|
|
|
|
"""Navigate to room page and open the sea overlay."""
|
|
|
|
|
self.create_pre_authenticated_session("founder@test.io")
|
|
|
|
|
self.browser.get(self.room_url)
|
|
|
|
|
btn = self.wait_for(lambda: self.browser.find_element(By.ID, "id_pick_sea_btn"))
|
|
|
|
|
self.browser.execute_script("arguments[0].click()", btn)
|
|
|
|
|
self.wait_for(lambda: self.browser.find_element(By.ID, "id_sea_overlay"))
|
|
|
|
|
|
|
|
|
|
# ── Button presence ───────────────────────────────────────────────── #
|
|
|
|
|
|
|
|
|
|
def test_deal_btn_absent(self):
|
|
|
|
|
"""DEAL btn replaced by deck stacks + LOCK HAND."""
|
|
|
|
|
self._load_sea_overlay()
|
|
|
|
|
self.assertEqual(self.browser.find_elements(By.ID, "id_sea_deal"), [])
|
|
|
|
|
|
|
|
|
|
def test_lock_hand_btn_present_and_disabled(self):
|
|
|
|
|
"""LOCK HAND btn is present but disabled before any cards are drawn."""
|
|
|
|
|
self._load_sea_overlay()
|
|
|
|
|
lock_btn = self.wait_for(
|
|
|
|
|
lambda: self.browser.find_element(By.ID, "id_sea_lock_hand")
|
|
|
|
|
)
|
|
|
|
|
self.assertFalse(lock_btn.is_enabled())
|
|
|
|
|
|
|
|
|
|
def test_del_btn_present(self):
|
|
|
|
|
"""DEL btn is always present in the sea overlay."""
|
|
|
|
|
self._load_sea_overlay()
|
|
|
|
|
self.wait_for(lambda: self.browser.find_element(By.ID, "id_sea_del"))
|
|
|
|
|
|
|
|
|
|
# ── Deck stacks ───────────────────────────────────────────────────── #
|
|
|
|
|
|
|
|
|
|
def test_two_deck_stacks_present(self):
|
|
|
|
|
"""Both levity and gravity deck stacks are visible."""
|
|
|
|
|
self._load_sea_overlay()
|
|
|
|
|
self.wait_for(lambda: self.browser.find_element(
|
|
|
|
|
By.CSS_SELECTOR, ".sea-deck-stack--levity"
|
|
|
|
|
))
|
|
|
|
|
self.wait_for(lambda: self.browser.find_element(
|
|
|
|
|
By.CSS_SELECTOR, ".sea-deck-stack--gravity"
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
def test_clicking_stack_shows_ok_btn(self):
|
|
|
|
|
"""Clicking a deck stack reveals its OK btn."""
|
|
|
|
|
self._load_sea_overlay()
|
|
|
|
|
stack = self.wait_for(lambda: self.browser.find_element(
|
|
|
|
|
By.CSS_SELECTOR, ".sea-deck-stack--levity"
|
|
|
|
|
))
|
|
|
|
|
self.browser.execute_script("arguments[0].click()", stack)
|
|
|
|
|
ok_btn = self.wait_for(lambda: self.browser.find_element(
|
|
|
|
|
By.CSS_SELECTOR, ".sea-deck-stack--levity .sea-stack-ok"
|
|
|
|
|
))
|
|
|
|
|
self.assertTrue(ok_btn.is_displayed())
|
|
|
|
|
|
|
|
|
|
def test_clicking_elsewhere_hides_ok_btn(self):
|
|
|
|
|
"""Clicking outside a focused stack dismisses the OK btn."""
|
|
|
|
|
self._load_sea_overlay()
|
|
|
|
|
stack = self.wait_for(lambda: self.browser.find_element(
|
|
|
|
|
By.CSS_SELECTOR, ".sea-deck-stack--levity"
|
|
|
|
|
))
|
|
|
|
|
self.browser.execute_script("arguments[0].click()", stack)
|
|
|
|
|
self.wait_for(lambda: self.browser.find_element(
|
|
|
|
|
By.CSS_SELECTOR, ".sea-deck-stack--levity .sea-stack-ok"
|
|
|
|
|
).is_displayed())
|
|
|
|
|
# Click the sea cards column (not a stack)
|
|
|
|
|
col = self.browser.find_element(By.CSS_SELECTOR, ".sea-cards-col")
|
|
|
|
|
self.browser.execute_script("arguments[0].click()", col)
|
|
|
|
|
self.wait_for(lambda: not any(
|
|
|
|
|
el.is_displayed()
|
|
|
|
|
for el in self.browser.find_elements(By.CSS_SELECTOR, ".sea-stack-ok")
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
# ── Card draw ─────────────────────────────────────────────────────── #
|
|
|
|
|
|
|
|
|
|
def test_ok_click_fills_cover_position(self):
|
|
|
|
|
"""First OK click places a card in the Cover position."""
|
|
|
|
|
self._load_sea_overlay()
|
|
|
|
|
stack = self.wait_for(lambda: self.browser.find_element(
|
|
|
|
|
By.CSS_SELECTOR, ".sea-deck-stack--levity"
|
|
|
|
|
))
|
|
|
|
|
self.browser.execute_script("arguments[0].click()", stack)
|
|
|
|
|
ok_btn = self.wait_for(lambda: self.browser.find_element(
|
|
|
|
|
By.CSS_SELECTOR, ".sea-deck-stack--levity .sea-stack-ok"
|
|
|
|
|
))
|
|
|
|
|
self.browser.execute_script("arguments[0].click()", ok_btn)
|
|
|
|
|
self.wait_for(lambda: self.browser.find_element(
|
|
|
|
|
By.CSS_SELECTOR, ".sea-pos-cover .sea-card-slot--filled"
|
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
def test_lock_hand_enables_after_six_draws(self):
|
|
|
|
|
"""LOCK HAND btn becomes enabled once all 6 positions are filled."""
|
|
|
|
|
self._load_sea_overlay()
|
|
|
|
|
|
|
|
|
|
for _ in range(6):
|
|
|
|
|
stack = self.wait_for(lambda: self.browser.find_element(
|
|
|
|
|
By.CSS_SELECTOR, ".sea-deck-stack--levity"
|
|
|
|
|
))
|
|
|
|
|
self.browser.execute_script("arguments[0].click()", stack)
|
|
|
|
|
ok_btn = self.wait_for(lambda: self.browser.find_element(
|
|
|
|
|
By.CSS_SELECTOR, ".sea-deck-stack--levity .sea-stack-ok"
|
|
|
|
|
))
|
|
|
|
|
self.browser.execute_script("arguments[0].click()", ok_btn)
|
|
|
|
|
|
|
|
|
|
lock_btn = self.wait_for(
|
|
|
|
|
lambda: self.browser.find_element(By.ID, "id_sea_lock_hand")
|
|
|
|
|
)
|
|
|
|
|
self.assertTrue(lock_btn.is_enabled())
|
|
|
|
|
|
|
|
|
|
def test_del_clears_drawn_cards(self):
|
|
|
|
|
"""DEL btn clears all drawn cards and resets positions to empty."""
|
|
|
|
|
self._load_sea_overlay()
|
|
|
|
|
# Draw one card
|
|
|
|
|
stack = self.wait_for(lambda: self.browser.find_element(
|
|
|
|
|
By.CSS_SELECTOR, ".sea-deck-stack--levity"
|
|
|
|
|
))
|
|
|
|
|
self.browser.execute_script("arguments[0].click()", stack)
|
|
|
|
|
ok_btn = self.wait_for(lambda: self.browser.find_element(
|
|
|
|
|
By.CSS_SELECTOR, ".sea-deck-stack--levity .sea-stack-ok"
|
|
|
|
|
))
|
|
|
|
|
self.browser.execute_script("arguments[0].click()", ok_btn)
|
|
|
|
|
self.wait_for(lambda: self.browser.find_element(
|
|
|
|
|
By.CSS_SELECTOR, ".sea-pos-cover .sea-card-slot--filled"
|
|
|
|
|
))
|
|
|
|
|
# DEL clears it
|
|
|
|
|
del_btn = self.browser.find_element(By.ID, "id_sea_del")
|
|
|
|
|
self.browser.execute_script("arguments[0].click()", del_btn)
|
|
|
|
|
self.wait_for(lambda: not self.browser.find_elements(
|
|
|
|
|
By.CSS_SELECTOR, ".sea-card-slot--filled"
|
|
|
|
|
))
|