FT fixes for polish-9 spec changes — CI #338 surfaced 4 stale assertions; sig-gate Brief race exposed by removing implicit wait
All checks were successful
ci/woodpecker/push/pyswiss Pipeline was successful
ci/woodpecker/push/main Pipeline was successful

CI pipeline #338 caught 4 FT failures cascading from yesterday's polish-9 + applet realignment commits (955bdc7, 652cef0). All four are stale assertions in FT code — no production code changes needed. ITs were already updated in the original commits; missed the parallel FT updates.

**(1) `test_gear_btn_opens_menu_with_nvm_only`** (test_game_my_sea.py:1851) — NVM btn changed `<a class="btn" href="...">` → `<button onclick="location.href=...">` (per [[feedback-btn-vs-anchor-font-family]] sans-serif fix). FT was reading `href` attr (returns None on buttons → `TypeError: argument of type 'NoneType' is not iterable`). Switched to read `onclick` attr (w. `or ""` guard against None).

**(2) `test_del_btn_is_disabled_until_hand_complete`** (test_game_my_sea.py:982 → renamed) — DEL btn now un-disables on the FIRST draw, not at hand completion (per the new state-machine spec, `_setHasDrawn(true)` fires on first deposit + AUTO DRAW POST-commit). Renamed → `test_del_btn_is_disabled_until_first_draw`; inverted the mid-draw assertion (was: still-disabled after 1 draw → now: un-disables immediately after 1 draw); kept the post-completion check (DEL stays enabled).

**(3) `test_carte_blanche_equip_and_multi_slot_gatekeeper`** (test_trinket_carte_blanche.py:89) — TWO issues here, only the first was symptomatic in CI:
  - Step 2 used `#id_kit_free_token` on /gameboard/ as a "non-trinket, no mini-tooltip" demo target. Free Token moved off Game Kit applet to Wallet applet per the equippables-only spec; no non-equippable icon left on Game Kit to demo w. Dropped step 2 entirely — the test's primary thing (Carte multi-slot equip flow at steps 3+) is intact.
  - SECOND-ORDER issue uncovered when (1) above stopped masking it: the deleted step 2 used to provide a ~5+ second wait (find Free Token + hover + wait for tooltip portal). That wait was enough for the auto-firing `.my-sea-sign-gate-brief` (slides in on /gameboard/ for users w/o a sig via the My Sea applet's `{% include _my_sea_sign_gate_brief.html %}` branch) to settle. Without the wait, the Brief is mid-slide when step 8 tries to click `id_create_game_btn` → `ElementClickInterceptedException` (Brief obscures button). Added explicit `.my-sea-sign-gate-brief .btn-cancel` wait-then-click between steps 1 + 3 to dismiss the Brief before proceeding.

**(4) `test_game_kit_panel_shows_token_inventory`** (test_gameboard.py:74) — TWO issues here too:
  - Step 7's `#id_kit_free_token` Free Token tooltip assertion. Same removal as (3). Replaced w. a NEGATIVE assertion that the element does NOT exist on Game Kit (regression guard against accidentally re-adding non-equippable items).
  - SECOND-ORDER again: step 9's `#id_kit_card_deck` check was a stale assertion that predated the `apps/lyric/models.py:540` `unlocked_decks.add(earthman)` post_save signal. `id_kit_card_deck` is the `{% empty %}`-branch placeholder, only rendered when `deck_variants` is empty. `capman@test.io` (the test fixture user) gets Earthman auto-unlocked → the concrete `id_kit_earthman_deck` renders instead. This was a latent stale assertion that only surfaced now because step 7's Free Token failure used to short-circuit the test before it reached step 9. Switched check to `id_kit_earthman_deck`.

Pattern worth noting for future cross-cutting refactors: when a test step has a side-effect wait (`wait_for(... tooltip displayed ...)`), removing it can unmask sig-gate / palette / Brief banners that auto-slide in on page load. The Brief race in (3) wasn't a NEW bug introduced by polish-9; it was always there, masked by the timing of the removed step. Same for the stale `id_kit_card_deck` assertion — predates the signal change; only surfaced when the failure cascade moved past it.

Discipline note for this session: user explicitly overrode [[feedback-ft-run-discipline]] when "specifically working on FTs, new or old" — ran each fix locally by full dotted path to verify before committing. All 4 green locally (8-16s each).

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:
Disco DeDisco
2026-05-26 02:34:02 -04:00
parent 652cef09c0
commit a133a9c1c3
3 changed files with 56 additions and 33 deletions

View File

@@ -969,23 +969,26 @@ class MySeaCardDrawTest(FunctionalTest):
# ── Test 6 ───────────────────────────────────────────────────────────────
def test_del_btn_is_disabled_until_hand_complete(self):
"""Iter-4c — DEL btn renders `.btn-disabled` server-side until
the hand is complete (per spec: the 24h free-draw quota is
committed at first-card-draw, can't be refunded by an early
DEL). Once the hand fills, JS removes `.btn-disabled` from DEL."""
def test_del_btn_is_disabled_until_first_draw(self):
"""User spec 2026-05-26: DEL btn renders `.btn-disabled` only
UNTIL the first card lands (was: until hand complete). The 24h
free-draw quota is still committed at first-card-draw + can't be
refunded by an early DEL, but the user can DEL the in-progress
hand to start over within the cycle. JS `_setHasDrawn(true)` fires
on the first deposit + at the AUTO DRAW POST-commit moment."""
picker = self._enter_picker_phase()
delbtn = picker.find_element(By.CSS_SELECTOR, "#id_sea_del")
# Pre-draw — disabled.
self.assertIn("btn-disabled", delbtn.get_attribute("class"))
self._draw_one(picker, "levity")
# Mid-draw — still disabled.
self.assertIn("btn-disabled", delbtn.get_attribute("class"))
self._draw_one(picker, "levity")
self._draw_one(picker, "gravity")
# Hand complete — DEL un-disables (clicking now opens guard portal).
# First card landed — DEL un-disables immediately.
self.wait_for(
lambda: self.assertNotIn("btn-disabled", delbtn.get_attribute("class"))
)
# Subsequent draws + completion — DEL stays enabled.
self._draw_one(picker, "levity")
self._draw_one(picker, "gravity")
self.assertNotIn("btn-disabled", delbtn.get_attribute("class"))
# ── Test 7 ───────────────────────────────────────────────────────────────
@@ -1846,9 +1849,12 @@ class MySeaGearBtnTest(FunctionalTest):
lambda: self.browser.find_element(By.ID, "id_my_sea_menu")
)
self.assertEqual(menu.value_of_css_property("display"), "block")
# NVM link present, DEL + BYE deliberately absent.
# NVM btn present, DEL + BYE deliberately absent. NVM is a `<button
# onclick="location.href=...">` (NOT an `<a class="btn">`) per 2026-
# 05-26 fix — anchors inherit body's serif font, buttons stay sans-
# serif. Check the onclick attr for the nav target instead of href.
nvm = menu.find_element(By.CSS_SELECTOR, ".btn-cancel")
self.assertIn("/gameboard/", nvm.get_attribute("href"))
self.assertIn("/gameboard/", nvm.get_attribute("onclick") or "")
self.assertEqual(
len(menu.find_elements(By.CSS_SELECTOR, ".btn-danger")), 0,
)