fix: AUTO-DRAWn my-sea cards are now clickable to re-open the stage modal — SeaDeal.register(card, posSelector, isLevity) public method populates _seaHand + delegates to SeaDeal's internal _fillSlot so the overlay click handler can resolve _seaHand[pos] for auto-drawn slots (previously short-circuited → silent no-op). AUTO DRAW in my_sea.html now calls register instead of the inline _fillSlot shim — also fixes a dataset.posKey inconsistency (inline stored raw "cover", SeaDeal stores ".sea-pos-cover"; click handler reads SeaDeal's form). User-reported 2026-05-21. TDD — new FT test_auto_drawn_slots_can_reopen_stage_modal_on_click pins the contract
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -112,6 +112,19 @@ var SeaDeal = (function () {
|
|||||||
_showStage(isLevity);
|
_showStage(isLevity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Like `openStage` but DOESN'T show the stage modal — used by AUTO
|
||||||
|
// DRAW (my_sea.html) to place cards quietly while keeping them
|
||||||
|
// clickable later. The overlay click handler reads `_seaHand[pos]`;
|
||||||
|
// without an entry here, click silently no-ops (the user-reported
|
||||||
|
// bug fixed 2026-05-21). Routing slot fill thru SeaDeal's internal
|
||||||
|
// `_fillSlot` (instead of the inline shim) also ensures
|
||||||
|
// `dataset.posKey` stays consistent w. the manual-FLIP path
|
||||||
|
// (selector form like ".sea-pos-cover", not raw "cover").
|
||||||
|
function register(card, posSelector, isLevity) {
|
||||||
|
_seaHand[posSelector] = { card: card, isLevity: isLevity };
|
||||||
|
_fillSlot(posSelector, card, isLevity);
|
||||||
|
}
|
||||||
|
|
||||||
// ── Init ──────────────────────────────────────────────────────────────────
|
// ── Init ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
@@ -239,6 +252,7 @@ var SeaDeal = (function () {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
openStage: openStage,
|
openStage: openStage,
|
||||||
|
register: register,
|
||||||
resetHand: resetHand,
|
resetHand: resetHand,
|
||||||
reinit: init, // call after overlay is injected into the DOM
|
reinit: init, // call after overlay is injected into the DOM
|
||||||
_testInit: function () {
|
_testInit: function () {
|
||||||
|
|||||||
@@ -15,6 +15,14 @@ from apps.epic.models import personal_sig_cards
|
|||||||
from apps.lyric.models import User
|
from apps.lyric.models import User
|
||||||
|
|
||||||
|
|
||||||
|
def _count_filled_slots(picker):
|
||||||
|
"""Count `.sea-card-slot--filled` elements inside a picker container.
|
||||||
|
Module-level so multiple test classes can use it without inheriting."""
|
||||||
|
return len(
|
||||||
|
picker.find_elements(By.CSS_SELECTOR, ".sea-card-slot--filled")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _seed_gameboard_applets():
|
def _seed_gameboard_applets():
|
||||||
"""My Sea + the rest of the gameboard applets so /gameboard/ renders
|
"""My Sea + the rest of the gameboard applets so /gameboard/ renders
|
||||||
without missing-applet errors during the applet-side assertions.
|
without missing-applet errors during the applet-side assertions.
|
||||||
@@ -840,6 +848,60 @@ class MySeaCardDrawTest(FunctionalTest):
|
|||||||
)
|
)
|
||||||
self.assertIn("GATE", action_btn.text.upper())
|
self.assertIn("GATE", action_btn.text.upper())
|
||||||
|
|
||||||
|
# ── Test 5b — AUTO DRAW slot click reopens preview modal ────────────────
|
||||||
|
|
||||||
|
def test_auto_drawn_slots_can_reopen_stage_modal_on_click(self):
|
||||||
|
"""Iter-6c bug-fix (user-reported 2026-05-21): cards placed by
|
||||||
|
AUTO DRAW were silently un-clickable — the inline `_fillSlot`
|
||||||
|
bypassed `SeaDeal.openStage` so SeaDeal's `_seaHand` dict was
|
||||||
|
never populated for those slots, and the overlay click handler
|
||||||
|
short-circuits on `if (!_seaHand[pos]) return;` → no modal.
|
||||||
|
|
||||||
|
Reproduce: draw one card manually (sets `_seaHand[lay]`), AUTO
|
||||||
|
DRAW the rest (sets `_seaHand[lay]` still, but NOT cover/crown
|
||||||
|
if the bug exists), then click an auto-drawn slot — should
|
||||||
|
re-open the stage modal w. that card's preview.
|
||||||
|
|
||||||
|
Fix landed: `SeaDeal.register(card, posSelector, isLevity)`
|
||||||
|
public method populates `_seaHand` + delegates to SeaDeal's
|
||||||
|
internal `_fillSlot` (so `dataset.posKey` stays consistent w.
|
||||||
|
the manual-FLIP path). AUTO DRAW now calls register, not the
|
||||||
|
inline shim."""
|
||||||
|
picker = self._enter_picker_phase()
|
||||||
|
# Manual draw: lay (first position in SAO order).
|
||||||
|
self._draw_one(picker, "levity")
|
||||||
|
self.assertEqual(_count_filled_slots(picker), 1)
|
||||||
|
|
||||||
|
# AUTO DRAW the remaining 2 (cover + crown).
|
||||||
|
action_btn = picker.find_element(By.CSS_SELECTOR, "#id_sea_action_btn")
|
||||||
|
action_btn.click()
|
||||||
|
confirm = self.wait_for(
|
||||||
|
lambda: self.browser.find_element(
|
||||||
|
By.CSS_SELECTOR, "#id_guard_portal.active .guard-yes"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
confirm.click()
|
||||||
|
# Wait for hand-complete transition (action btn → GATE VIEW).
|
||||||
|
self.wait_for(
|
||||||
|
lambda: self.assertEqual(action_btn.get_attribute("data-state"), "gate-view")
|
||||||
|
)
|
||||||
|
# All 3 slots filled.
|
||||||
|
self.wait_for(lambda: self.assertEqual(_count_filled_slots(picker), 3))
|
||||||
|
|
||||||
|
# Click an AUTO-drawn slot (cover — second in SAO draw order, so
|
||||||
|
# placed by AUTO DRAW). Two-tap pattern: first click focuses,
|
||||||
|
# second click opens the modal (per SeaDeal's overlay handler).
|
||||||
|
cover_slot = picker.find_element(
|
||||||
|
By.CSS_SELECTOR, ".sea-pos-cover .sea-card-slot--filled"
|
||||||
|
)
|
||||||
|
cover_slot.click()
|
||||||
|
cover_slot.click()
|
||||||
|
# Stage modal must open w. the card preview.
|
||||||
|
stage = self.wait_for(
|
||||||
|
lambda: self.browser.find_element(By.CSS_SELECTOR, "#id_sea_stage")
|
||||||
|
)
|
||||||
|
self.wait_for(lambda: self.assertTrue(stage.is_displayed()))
|
||||||
|
|
||||||
# ── Test 6 ───────────────────────────────────────────────────────────────
|
# ── Test 6 ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def test_del_btn_is_disabled_until_hand_complete(self):
|
def test_del_btn_is_disabled_until_hand_complete(self):
|
||||||
|
|||||||
@@ -591,7 +591,20 @@
|
|||||||
);
|
);
|
||||||
if (stack) _showOk(stack);
|
if (stack) _showOk(stack);
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
_fillSlot(e.posName, e.card, e.isLevity);
|
// Route thru SeaDeal.register (not the inline
|
||||||
|
// `_fillSlot`) so SeaDeal's `_seaHand` dict
|
||||||
|
// gets the entry — the overlay click handler
|
||||||
|
// reads from there, so without registration
|
||||||
|
// the auto-drawn slots are silently un-
|
||||||
|
// clickable (user-reported bug, 2026-05-21).
|
||||||
|
// Fallback to the inline shim only if sea.js
|
||||||
|
// hasn't loaded yet (defensive — script load
|
||||||
|
// order makes this unlikely).
|
||||||
|
if (window.SeaDeal && window.SeaDeal.register) {
|
||||||
|
SeaDeal.register(e.card, '.sea-pos-' + e.posName, e.isLevity);
|
||||||
|
} else {
|
||||||
|
_fillSlot(e.posName, e.card, e.isLevity);
|
||||||
|
}
|
||||||
cross.querySelector('.sea-pos-' + e.posName + ' .sea-card-slot')
|
cross.querySelector('.sea-pos-' + e.posName + ' .sea-card-slot')
|
||||||
.classList.add('sea-card-slot--visible');
|
.classList.add('sea-card-slot--visible');
|
||||||
_filled++;
|
_filled++;
|
||||||
|
|||||||
Reference in New Issue
Block a user