Files
python-tdd/src/functional_tests/test_trinket_carte_blanche.py

386 lines
17 KiB
Python
Raw Normal View History

from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from .base import FunctionalTest
from apps.applets.models import Applet
from apps.lyric.models import Token, User
class CarteBlancheTest(FunctionalTest):
def setUp(self):
super().setUp()
for slug, name, cols, rows in [
("new-game", "New Game", 6, 3),
("my-games", "My Games", 6, 3),
("game-kit", "Game Kit", 6, 3),
]:
Applet.objects.get_or_create(
slug=slug,
defaults={
"name": name, "grid_cols": cols,
"grid_rows": rows, "context": "gameboard",
},
)
# is_staff triggers COIN + FREE + PASS via post_save signal; PASS auto-equipped
self.gamer = User.objects.create(email="blanche@test.io", is_staff=True)
Token.objects.create(user=self.gamer, token_type=Token.TITHE)
self.carte = Token.objects.create(user=self.gamer, token_type=Token.CARTE)
# ── Test 1 ───────────────────────────────────────────────────────────────
def test_equipped_pass_shows_mini_tooltip_in_game_kit(self):
# 1. Log in, land on dashboard
self.create_pre_authenticated_session("blanche@test.io")
self.browser.get(self.live_server_url)
# 2. Open kit bag — Backstage Pass visible in Trinkets section
self.wait_for(lambda: self.browser.find_element(By.ID, "id_kit_btn")).click()
self.wait_for(
lambda: self.browser.find_element(
By.CSS_SELECTOR,
f'#id_kit_bag_dialog [data-token-type="{Token.PASS}"]',
)
)
# 3. Navigate to gameboard (use get() — kit bag dialog still open and
# would intercept a click on the footer nav link in headless Firefox)
self.browser.get(self.live_server_url + "/gameboard/")
self.wait_for(
lambda: self.assertRegex(self.browser.current_url, r"/gameboard/$")
)
# 4. Find Backstage Pass in the Game Kit applet
self.wait_for(lambda: self.browser.find_element(By.ID, "id_game_kit"))
pass_el = self.browser.find_element(By.ID, "id_kit_pass")
self.browser.execute_script(
"arguments[0].scrollIntoView({block: 'center'})", pass_el
)
# 5. Hover over Pass — main tooltip appears via portal
ActionChains(self.browser).move_to_element(pass_el).perform()
self.wait_for(
lambda: self.assertTrue(
self.browser.find_element(By.ID, "id_tooltip_portal").is_displayed()
)
)
portal = self.browser.find_element(By.ID, "id_tooltip_portal")
self.assertIn("Backstage Pass", portal.text)
self.assertIn("Admit All Entry", portal.text)
# 6. A mini tooltip appears below the main tooltip, flush with its right edge.
# Since Pass is the equipped trinket, it says "Equipped."
mini = self.browser.find_element(By.ID, "id_mini_tooltip_portal")
self.wait_for(lambda: self.assertTrue(mini.is_displayed()))
self.assertIn("Equipped", mini.text)
portal_rect = self.browser.execute_script(
"return arguments[0].getBoundingClientRect()", portal
)
mini_rect = self.browser.execute_script(
"return arguments[0].getBoundingClientRect()", mini
)
self.assertGreater(mini_rect["top"], portal_rect["bottom"] - 5) # below main
self.assertAlmostEqual(mini_rect["right"], portal_rect["right"], delta=10) # flush right
# ── Test 2 ───────────────────────────────────────────────────────────────
def test_carte_blanche_equip_and_multi_slot_gatekeeper(self):
# 1. Log in, navigate directly to gameboard
self.create_pre_authenticated_session("blanche@test.io")
self.browser.get(self.live_server_url + "/gameboard/")
self.wait_for(lambda: self.browser.find_element(By.ID, "id_game_kit"))
# 2. Hover over Free Token — no mini tooltip (not a trinket, no data-token-id)
el = self.browser.find_element(By.ID, "id_kit_free_token")
ActionChains(self.browser).move_to_element(el).perform()
self.wait_for(
lambda: self.browser.find_element(By.ID, "id_tooltip_portal").is_displayed()
)
self.assertFalse(
self.browser.find_element(By.ID, "id_mini_tooltip_portal").is_displayed()
)
# Coin-on-a-String IS equippable (has data-token-id) — mini tooltip shows
coin_el = self.browser.find_element(By.ID, "id_kit_coin_on_a_string")
ActionChains(self.browser).move_to_element(coin_el).perform()
self.wait_for(
lambda: self.browser.find_element(By.ID, "id_tooltip_portal").is_displayed()
)
self.wait_for(
lambda: self.assertTrue(
self.browser.find_element(By.ID, "id_mini_tooltip_portal").is_displayed()
)
)
# 3. Hover Carte Blanche — main tooltip present; mini shows "Not Equipped"; DON active
carte_el = self.browser.find_element(By.ID, "id_kit_carte_blanche")
self.browser.execute_script(
"arguments[0].scrollIntoView({block: 'center'})", carte_el
)
ActionChains(self.browser).move_to_element(carte_el).perform()
self.wait_for(
lambda: self.browser.find_element(By.ID, "id_tooltip_portal").is_displayed()
)
portal = self.browser.find_element(By.ID, "id_tooltip_portal")
self.assertIn("Carte Blanche", portal.text)
self.assertIn("Admit up to +6", portal.text)
self.assertIn("taking over from here", portal.text)
self.assertIn("no expiry", portal.text)
mini = self.browser.find_element(By.ID, "id_mini_tooltip_portal")
self.wait_for(lambda: self.assertTrue(mini.is_displayed()))
self.assertIn("Not Equipped", mini.text)
don = portal.find_element(By.CSS_SELECTOR, ".btn-equip")
self.assertNotIn("btn-disabled", don.get_attribute("class"))
# 4. Click DON — DON becomes disabled; data-equipped-id set optimistically
don.click()
self.wait_for(
lambda: self.assertIn(
"btn-disabled",
portal.find_element(By.CSS_SELECTOR, ".btn-equip").get_attribute("class"),
)
)
# 5. JS-side optimistic update: data-equipped-id reflects Carte immediately
game_kit = self.browser.find_element(By.ID, "id_game_kit")
self.wait_for(
lambda: self.assertEqual(
game_kit.get_attribute("data-equipped-id"),
str(self.carte.pk),
)
)
# 6. Hover Backstage Pass — mini shows "Not Equipped" (Pass no longer equipped)
pass_el = self.browser.find_element(By.ID, "id_kit_pass")
ActionChains(self.browser).move_to_element(pass_el).perform()
self.wait_for(
lambda: self.assertIn(
"Backstage Pass",
self.browser.find_element(By.ID, "id_tooltip_portal").text,
)
)
mini = self.browser.find_element(By.ID, "id_mini_tooltip_portal")
self.wait_for(lambda: self.assertTrue(mini.is_displayed()))
self.assertIn("Not Equipped", mini.text)
# ── GATEKEEPER PHASE ─────────────────────────────────────────────────
# 8. Create a new game room via the New Game applet
self.browser.find_element(By.ID, "id_new_game_name").send_keys("The Long Room")
self.browser.find_element(By.ID, "id_create_game_btn").click()
self.wait_for(
lambda: self.assertIn("/gameboard/room/", self.browser.current_url)
)
self.wait_for(
lambda: self.assertIn("/gate/", self.browser.current_url)
)
fix CI FT regressions: deck contribution, ROLE SELECT no-deck guard, sig qualifiers, Carte Blanche multi-slot - test_deck_contribution: get_or_create _equip_earthman + unlocked_decks.add; slot_number=2 on _setup_in_use_deck seat; navigate to /gameboard/ (not gate — game-kit panel absent there); drop #id_kit_card_deck click ({% empty %} placeholder; deck renders in loop when present); use textContent for CSS-hidden tooltip; drop stale .deck-micro-status assertion (now mini-portal) - ROLE SELECT FTs (RoleSelectTest + RoleSelectTrayTest): equip Earthman deck for active-slot user in each test that opens the fan — fixes no-deck JS guard blocking #id_role_select - test_room_sig_select: seed The Nomad/Schizo w. correct Earthman slugs/names + Enlightened/ Engraven qualifiers; grant super-nomad + super-schizo Notes to all gamers so Major Arcana appear in overlay; seed Middle Arcana w. Elevated/Graven qualifiers; rename test methods - test_game_kit: drop stale assertIn("active", text) — availability moved to In-Use mini-portal - Carte Blanche: CB stays equipped after multi-slot deposit (revert drop_token unequip); select_role existing-seat query gains order_by("slot_number") for deterministic primary seat; multi-slot FT: kit bag shows placeholder after first deposit (CB unequipped); cold-feet verifies DON via hover→portal; re-equip via portal DON before re-deposit; new test_carte_in_use_game_kit_shows_room_attribution checks Game Kit tooltip after deposit Code architected by Disco DeDisco <discodedisco@outlook.com> Git commit message Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 16:29:51 -04:00
gate_url = self.browser.current_url
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".gate-overlay")
)
def open_kit_and_select_carte():
self.browser.find_element(By.ID, "id_kit_btn").click()
self.wait_for(
lambda: self.browser.find_element(
By.CSS_SELECTOR,
f'#id_kit_bag_dialog [data-token-type="{Token.CARTE}"]',
)
)
self.browser.find_element(
By.CSS_SELECTOR,
f'#id_kit_bag_dialog [data-token-type="{Token.CARTE}"]',
).click()
def deposit_carte():
self.browser.find_element(By.CSS_SELECTOR, "button.token-rails").click()
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".token-slot.claimed")
)
# 9. Open kit bag, select Carte Blanche, deposit via rails btn
open_kit_and_select_carte()
deposit_carte()
# Helper: always fetches a fresh gate-slot element (avoid stale refs after redirects)
def get_circle(i):
return self.browser.find_element(
By.CSS_SELECTOR, f".gate-slot[data-slot='{i + 1}']"
)
# 10. Return panel glows; circle 1 gains OK btn → click it → fills
return_panel = self.browser.find_element(By.CSS_SELECTOR, ".token-slot.claimed")
self.assertTrue(return_panel.is_displayed())
self.wait_for(
lambda: get_circle(0).find_element(By.CSS_SELECTOR, ".drop-token-btn")
).click()
self.wait_for(
lambda: self.assertIn("filled", get_circle(0).get_attribute("class"))
)
# 11. Return panel still glows; circle 2 now has OK btn
self.assertTrue(
self.browser.find_element(By.CSS_SELECTOR, ".token-slot.claimed").is_displayed()
)
self.wait_for(lambda: get_circle(1).find_element(By.CSS_SELECTOR, ".drop-token-btn"))
fix CI FT regressions: deck contribution, ROLE SELECT no-deck guard, sig qualifiers, Carte Blanche multi-slot - test_deck_contribution: get_or_create _equip_earthman + unlocked_decks.add; slot_number=2 on _setup_in_use_deck seat; navigate to /gameboard/ (not gate — game-kit panel absent there); drop #id_kit_card_deck click ({% empty %} placeholder; deck renders in loop when present); use textContent for CSS-hidden tooltip; drop stale .deck-micro-status assertion (now mini-portal) - ROLE SELECT FTs (RoleSelectTest + RoleSelectTrayTest): equip Earthman deck for active-slot user in each test that opens the fan — fixes no-deck JS guard blocking #id_role_select - test_room_sig_select: seed The Nomad/Schizo w. correct Earthman slugs/names + Enlightened/ Engraven qualifiers; grant super-nomad + super-schizo Notes to all gamers so Major Arcana appear in overlay; seed Middle Arcana w. Elevated/Graven qualifiers; rename test methods - test_game_kit: drop stale assertIn("active", text) — availability moved to In-Use mini-portal - Carte Blanche: CB stays equipped after multi-slot deposit (revert drop_token unequip); select_role existing-seat query gains order_by("slot_number") for deterministic primary seat; multi-slot FT: kit bag shows placeholder after first deposit (CB unequipped); cold-feet verifies DON via hover→portal; re-equip via portal DON before re-deposit; new test_carte_in_use_game_kit_shows_room_attribution checks Game Kit tooltip after deposit Code architected by Disco DeDisco <discodedisco@outlook.com> Git commit message Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 16:29:51 -04:00
# 12. Kit bag trinket section is now empty — Carte Blanche unequipped on deposit
self.browser.find_element(By.ID, "id_kit_btn").click()
self.wait_for(
lambda: self.browser.find_element(
fix CI FT regressions: deck contribution, ROLE SELECT no-deck guard, sig qualifiers, Carte Blanche multi-slot - test_deck_contribution: get_or_create _equip_earthman + unlocked_decks.add; slot_number=2 on _setup_in_use_deck seat; navigate to /gameboard/ (not gate — game-kit panel absent there); drop #id_kit_card_deck click ({% empty %} placeholder; deck renders in loop when present); use textContent for CSS-hidden tooltip; drop stale .deck-micro-status assertion (now mini-portal) - ROLE SELECT FTs (RoleSelectTest + RoleSelectTrayTest): equip Earthman deck for active-slot user in each test that opens the fan — fixes no-deck JS guard blocking #id_role_select - test_room_sig_select: seed The Nomad/Schizo w. correct Earthman slugs/names + Enlightened/ Engraven qualifiers; grant super-nomad + super-schizo Notes to all gamers so Major Arcana appear in overlay; seed Middle Arcana w. Elevated/Graven qualifiers; rename test methods - test_game_kit: drop stale assertIn("active", text) — availability moved to In-Use mini-portal - Carte Blanche: CB stays equipped after multi-slot deposit (revert drop_token unequip); select_role existing-seat query gains order_by("slot_number") for deterministic primary seat; multi-slot FT: kit bag shows placeholder after first deposit (CB unequipped); cold-feet verifies DON via hover→portal; re-equip via portal DON before re-deposit; new test_carte_in_use_game_kit_shows_room_attribution checks Game Kit tooltip after deposit Code architected by Disco DeDisco <discodedisco@outlook.com> Git commit message Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 16:29:51 -04:00
By.CSS_SELECTOR, "#id_kit_bag_dialog .kit-bag-placeholder"
)
)
self.browser.find_element(By.ID, "id_kit_btn").click()
self.wait_for(
lambda: self.assertFalse(
self.browser.find_element(By.ID, "id_kit_bag_dialog").is_displayed()
)
)
# 13. Cold feet: click return panel → Carte returned, return panel gone
self.browser.find_element(By.CSS_SELECTOR, ".token-return-btn").click()
self.wait_for(
lambda: self.assertEqual(
len(self.browser.find_elements(By.CSS_SELECTOR, ".token-slot.claimed")), 0
)
)
fix CI FT regressions: deck contribution, ROLE SELECT no-deck guard, sig qualifiers, Carte Blanche multi-slot - test_deck_contribution: get_or_create _equip_earthman + unlocked_decks.add; slot_number=2 on _setup_in_use_deck seat; navigate to /gameboard/ (not gate — game-kit panel absent there); drop #id_kit_card_deck click ({% empty %} placeholder; deck renders in loop when present); use textContent for CSS-hidden tooltip; drop stale .deck-micro-status assertion (now mini-portal) - ROLE SELECT FTs (RoleSelectTest + RoleSelectTrayTest): equip Earthman deck for active-slot user in each test that opens the fan — fixes no-deck JS guard blocking #id_role_select - test_room_sig_select: seed The Nomad/Schizo w. correct Earthman slugs/names + Enlightened/ Engraven qualifiers; grant super-nomad + super-schizo Notes to all gamers so Major Arcana appear in overlay; seed Middle Arcana w. Elevated/Graven qualifiers; rename test methods - test_game_kit: drop stale assertIn("active", text) — availability moved to In-Use mini-portal - Carte Blanche: CB stays equipped after multi-slot deposit (revert drop_token unequip); select_role existing-seat query gains order_by("slot_number") for deterministic primary seat; multi-slot FT: kit bag shows placeholder after first deposit (CB unequipped); cold-feet verifies DON via hover→portal; re-equip via portal DON before re-deposit; new test_carte_in_use_game_kit_shows_room_attribution checks Game Kit tooltip after deposit Code architected by Disco DeDisco <discodedisco@outlook.com> Git commit message Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 16:29:51 -04:00
# Carte reappears in Game Kit with DON available — not re-equipped automatically.
# Game Kit is only in the /gameboard/ context, not the gatekeeper page.
self.browser.get(self.live_server_url + "/gameboard/")
carte_el = self.browser.find_element(By.ID, "id_kit_carte_blanche")
self.browser.execute_script(
"arguments[0].scrollIntoView({block:'center'})", carte_el
)
fix CI FT regressions: deck contribution, ROLE SELECT no-deck guard, sig qualifiers, Carte Blanche multi-slot - test_deck_contribution: get_or_create _equip_earthman + unlocked_decks.add; slot_number=2 on _setup_in_use_deck seat; navigate to /gameboard/ (not gate — game-kit panel absent there); drop #id_kit_card_deck click ({% empty %} placeholder; deck renders in loop when present); use textContent for CSS-hidden tooltip; drop stale .deck-micro-status assertion (now mini-portal) - ROLE SELECT FTs (RoleSelectTest + RoleSelectTrayTest): equip Earthman deck for active-slot user in each test that opens the fan — fixes no-deck JS guard blocking #id_role_select - test_room_sig_select: seed The Nomad/Schizo w. correct Earthman slugs/names + Enlightened/ Engraven qualifiers; grant super-nomad + super-schizo Notes to all gamers so Major Arcana appear in overlay; seed Middle Arcana w. Elevated/Graven qualifiers; rename test methods - test_game_kit: drop stale assertIn("active", text) — availability moved to In-Use mini-portal - Carte Blanche: CB stays equipped after multi-slot deposit (revert drop_token unequip); select_role existing-seat query gains order_by("slot_number") for deterministic primary seat; multi-slot FT: kit bag shows placeholder after first deposit (CB unequipped); cold-feet verifies DON via hover→portal; re-equip via portal DON before re-deposit; new test_carte_in_use_game_kit_shows_room_attribution checks Game Kit tooltip after deposit Code architected by Disco DeDisco <discodedisco@outlook.com> Git commit message Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 16:29:51 -04:00
ActionChains(self.browser).move_to_element(carte_el).perform()
portal = self.wait_for(
lambda: self.browser.find_element(By.ID, "id_tooltip_portal")
)
fix CI FT regressions: deck contribution, ROLE SELECT no-deck guard, sig qualifiers, Carte Blanche multi-slot - test_deck_contribution: get_or_create _equip_earthman + unlocked_decks.add; slot_number=2 on _setup_in_use_deck seat; navigate to /gameboard/ (not gate — game-kit panel absent there); drop #id_kit_card_deck click ({% empty %} placeholder; deck renders in loop when present); use textContent for CSS-hidden tooltip; drop stale .deck-micro-status assertion (now mini-portal) - ROLE SELECT FTs (RoleSelectTest + RoleSelectTrayTest): equip Earthman deck for active-slot user in each test that opens the fan — fixes no-deck JS guard blocking #id_role_select - test_room_sig_select: seed The Nomad/Schizo w. correct Earthman slugs/names + Enlightened/ Engraven qualifiers; grant super-nomad + super-schizo Notes to all gamers so Major Arcana appear in overlay; seed Middle Arcana w. Elevated/Graven qualifiers; rename test methods - test_game_kit: drop stale assertIn("active", text) — availability moved to In-Use mini-portal - Carte Blanche: CB stays equipped after multi-slot deposit (revert drop_token unequip); select_role existing-seat query gains order_by("slot_number") for deterministic primary seat; multi-slot FT: kit bag shows placeholder after first deposit (CB unequipped); cold-feet verifies DON via hover→portal; re-equip via portal DON before re-deposit; new test_carte_in_use_game_kit_shows_room_attribution checks Game Kit tooltip after deposit Code architected by Disco DeDisco <discodedisco@outlook.com> Git commit message Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 16:29:51 -04:00
self.wait_for(lambda: self.assertTrue(portal.is_displayed()))
don = self.wait_for(
lambda: portal.find_element(By.CSS_SELECTOR, ".btn-equip")
)
fix CI FT regressions: deck contribution, ROLE SELECT no-deck guard, sig qualifiers, Carte Blanche multi-slot - test_deck_contribution: get_or_create _equip_earthman + unlocked_decks.add; slot_number=2 on _setup_in_use_deck seat; navigate to /gameboard/ (not gate — game-kit panel absent there); drop #id_kit_card_deck click ({% empty %} placeholder; deck renders in loop when present); use textContent for CSS-hidden tooltip; drop stale .deck-micro-status assertion (now mini-portal) - ROLE SELECT FTs (RoleSelectTest + RoleSelectTrayTest): equip Earthman deck for active-slot user in each test that opens the fan — fixes no-deck JS guard blocking #id_role_select - test_room_sig_select: seed The Nomad/Schizo w. correct Earthman slugs/names + Enlightened/ Engraven qualifiers; grant super-nomad + super-schizo Notes to all gamers so Major Arcana appear in overlay; seed Middle Arcana w. Elevated/Graven qualifiers; rename test methods - test_game_kit: drop stale assertIn("active", text) — availability moved to In-Use mini-portal - Carte Blanche: CB stays equipped after multi-slot deposit (revert drop_token unequip); select_role existing-seat query gains order_by("slot_number") for deterministic primary seat; multi-slot FT: kit bag shows placeholder after first deposit (CB unequipped); cold-feet verifies DON via hover→portal; re-equip via portal DON before re-deposit; new test_carte_in_use_game_kit_shows_room_attribution checks Game Kit tooltip after deposit Code architected by Disco DeDisco <discodedisco@outlook.com> Git commit message Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 16:29:51 -04:00
self.assertNotIn("btn-disabled", don.get_attribute("class"))
# ── COLD FEET RESOLVED: full six-slot run ────────────────────────────
fix CI FT regressions: deck contribution, ROLE SELECT no-deck guard, sig qualifiers, Carte Blanche multi-slot - test_deck_contribution: get_or_create _equip_earthman + unlocked_decks.add; slot_number=2 on _setup_in_use_deck seat; navigate to /gameboard/ (not gate — game-kit panel absent there); drop #id_kit_card_deck click ({% empty %} placeholder; deck renders in loop when present); use textContent for CSS-hidden tooltip; drop stale .deck-micro-status assertion (now mini-portal) - ROLE SELECT FTs (RoleSelectTest + RoleSelectTrayTest): equip Earthman deck for active-slot user in each test that opens the fan — fixes no-deck JS guard blocking #id_role_select - test_room_sig_select: seed The Nomad/Schizo w. correct Earthman slugs/names + Enlightened/ Engraven qualifiers; grant super-nomad + super-schizo Notes to all gamers so Major Arcana appear in overlay; seed Middle Arcana w. Elevated/Graven qualifiers; rename test methods - test_game_kit: drop stale assertIn("active", text) — availability moved to In-Use mini-portal - Carte Blanche: CB stays equipped after multi-slot deposit (revert drop_token unequip); select_role existing-seat query gains order_by("slot_number") for deterministic primary seat; multi-slot FT: kit bag shows placeholder after first deposit (CB unequipped); cold-feet verifies DON via hover→portal; re-equip via portal DON before re-deposit; new test_carte_in_use_game_kit_shows_room_attribution checks Game Kit tooltip after deposit Code architected by Disco DeDisco <discodedisco@outlook.com> Git commit message Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 16:29:51 -04:00
# 14. Re-equip Carte via portal DON, navigate back to gate
don.click()
self.wait_for(
lambda: self.assertIn(
"btn-disabled",
portal.find_element(By.CSS_SELECTOR, ".btn-equip").get_attribute("class"),
)
)
self.browser.get(gate_url)
self.wait_for(lambda: self.browser.find_element(By.CSS_SELECTOR, ".gate-overlay"))
open_kit_and_select_carte()
deposit_carte()
# 15. Click OK on circle 1 → fills; circle 1 loses its OK btn; circle 2 gains one
self.wait_for(
lambda: get_circle(0).find_element(By.CSS_SELECTOR, ".drop-token-btn")
).click()
self.wait_for(
lambda: self.assertIn("filled", get_circle(0).get_attribute("class"))
)
self.wait_for(
lambda: self.assertEqual(
len(get_circle(0).find_elements(By.CSS_SELECTOR, ".drop-token-btn")), 0
)
)
self.wait_for(lambda: get_circle(1).find_element(By.CSS_SELECTOR, ".drop-token-btn"))
# 16. Click OK on circle 2 → turns to NVM; circle 3 gains OK
self.wait_for(
lambda: get_circle(1).find_element(By.CSS_SELECTOR, ".drop-token-btn")
).click()
self.wait_for(
lambda: self.assertEqual(
get_circle(1).find_element(By.CSS_SELECTOR, ".slot-release-btn").text.upper(),
"NVM",
)
)
self.wait_for(lambda: get_circle(2).find_element(By.CSS_SELECTOR, ".drop-token-btn"))
# 17. Click NVM on circle 2 → circle 2 regains OK; circle 3 loses it (strict n+1)
# Circle 1 still filled; return panel still glowing; lease name still present
self.wait_for(
lambda: get_circle(1).find_element(By.CSS_SELECTOR, ".slot-release-btn")
).click()
self.wait_for(lambda: get_circle(1).find_element(By.CSS_SELECTOR, ".drop-token-btn"))
self.assertIn("filled", get_circle(0).get_attribute("class"))
self.assertTrue(
self.browser.find_element(By.CSS_SELECTOR, ".token-slot.claimed").is_displayed()
)
self.assertEqual(
len(get_circle(2).find_elements(By.CSS_SELECTOR, ".drop-token-btn")), 0
)
# 18. Fill circles 2 → 6 sequentially; verify each subsequent OK appears
for i in range(1, 6):
self.wait_for(
lambda i=i: get_circle(i).find_element(By.CSS_SELECTOR, ".drop-token-btn")
).click()
self.wait_for(
lambda i=i: self.assertIn("filled", get_circle(i).get_attribute("class"))
)
if i < 5:
self.wait_for(
lambda i=i: get_circle(i + 1).find_element(
By.CSS_SELECTOR, ".drop-token-btn"
)
)
# 19. All six circles filled — launch btn appears
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".launch-game-btn")
)
fix CI FT regressions: deck contribution, ROLE SELECT no-deck guard, sig qualifiers, Carte Blanche multi-slot - test_deck_contribution: get_or_create _equip_earthman + unlocked_decks.add; slot_number=2 on _setup_in_use_deck seat; navigate to /gameboard/ (not gate — game-kit panel absent there); drop #id_kit_card_deck click ({% empty %} placeholder; deck renders in loop when present); use textContent for CSS-hidden tooltip; drop stale .deck-micro-status assertion (now mini-portal) - ROLE SELECT FTs (RoleSelectTest + RoleSelectTrayTest): equip Earthman deck for active-slot user in each test that opens the fan — fixes no-deck JS guard blocking #id_role_select - test_room_sig_select: seed The Nomad/Schizo w. correct Earthman slugs/names + Enlightened/ Engraven qualifiers; grant super-nomad + super-schizo Notes to all gamers so Major Arcana appear in overlay; seed Middle Arcana w. Elevated/Graven qualifiers; rename test methods - test_game_kit: drop stale assertIn("active", text) — availability moved to In-Use mini-portal - Carte Blanche: CB stays equipped after multi-slot deposit (revert drop_token unequip); select_role existing-seat query gains order_by("slot_number") for deterministic primary seat; multi-slot FT: kit bag shows placeholder after first deposit (CB unequipped); cold-feet verifies DON via hover→portal; re-equip via portal DON before re-deposit; new test_carte_in_use_game_kit_shows_room_attribution checks Game Kit tooltip after deposit Code architected by Disco DeDisco <discodedisco@outlook.com> Git commit message Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 16:29:51 -04:00
def test_carte_in_use_game_kit_shows_room_attribution(self):
"""While Carte Blanche is deposited in a room, its Game Kit tooltip
shows 'In game: <room name>' so the gamer knows where it's committed."""
self.create_pre_authenticated_session("blanche@test.io")
self.browser.get(self.live_server_url + "/gameboard/")
self.wait_for(lambda: self.browser.find_element(By.ID, "id_game_kit"))
# DON the Carte Blanche via tooltip portal
carte_el = self.browser.find_element(By.ID, "id_kit_carte_blanche")
self.browser.execute_script(
"arguments[0].scrollIntoView({block:'center'})", carte_el
)
ActionChains(self.browser).move_to_element(carte_el).perform()
portal = self.wait_for(
lambda: self.browser.find_element(By.ID, "id_tooltip_portal")
)
self.wait_for(lambda: self.assertTrue(portal.is_displayed()))
don = self.wait_for(lambda: portal.find_element(By.CSS_SELECTOR, ".btn-equip"))
don.click()
self.wait_for(
lambda: self.assertIn(
"btn-disabled",
portal.find_element(By.CSS_SELECTOR, ".btn-equip").get_attribute("class"),
)
)
# Create a room and deposit the Carte Blanche
self.browser.find_element(By.ID, "id_new_game_name").send_keys("Commitment Room")
self.browser.find_element(By.ID, "id_create_game_btn").click()
self.wait_for(lambda: self.assertIn("/gate/", self.browser.current_url))
self.browser.find_element(By.ID, "id_kit_btn").click()
self.wait_for(
lambda: self.browser.find_element(
By.CSS_SELECTOR,
f'#id_kit_bag_dialog [data-token-type="{Token.CARTE}"]',
)
).click()
self.browser.find_element(By.CSS_SELECTOR, "button.token-rails").click()
self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".token-slot.claimed")
)
# Game Kit panel is on /gameboard/, not the gate page — navigate back to check tooltip
self.browser.get(self.live_server_url + "/gameboard/")
carte_tt = self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, "#id_kit_carte_blanche .tt")
)
self.assertIn(
"Commitment Room",
carte_tt.get_attribute("textContent"),
)