functional_tests + CI: rename pass + structural consolidations + parallel test-FTs split — every FT file now starts with one of 6 prefixes (test_admin_* / test_bill_* / test_core_* / test_dash_* / test_game_room_* / test_trinket_*) plus the 4 page-roots test_billboard / test_dashboard / test_gameboard / test_jasmine, so the partition is unambiguous and stable for tooling (the previous mix of test_applet_*, test_room_*, test_component_*, ad-hoc names had no consistent grouping); session-side: merged test_gatekeeper_bud_btn.py into test_bud_btn.py (then user renamed to test_core_bud_btn.py) — both files drove the same #id_bud_btn UI in two contexts (post-share + gatekeeper invite) and shared the bud-btn.js skeleton, so consolidation was overdue; split test_component_cards_tarot.py into test_admin_tarot.py (just TarotAdminTest, sitting next to test_admin / test_admin_post_readonly) + 3 classes (TarotDeckTest / GameKitDeckSelectionTest / GameKitPageTest) appended to test_game_room_tray.py; updated stale test_bud_btn.py references in the test_core_bud_btn.py docstring + test_admin_post_readonly.py comment to point at the new filename; user-driven renames (22 files): test_applet_my_notes/posts → test_bill_my_*, test_applet_new_post[_line_validation] → test_bill_new_post[_line_validation], test_applet_my_sky → test_dash_my_sky, test_applet_palette → test_dash_palette, test_wallet → test_dash_wallet, test_login → test_core_login, test_navbar → test_core_navbar, test_sharing → test_core_sharing, test_layout_and_styling → test_core_styling, test_my_buds → test_bill_my_buds, test_bud_btn → test_core_bud_btn, test_deck_contribution → test_game_room_deck_contrib, test_game_invite → test_game_room_invite, test_room_gatekeeper → test_game_room_gatekeeper, test_room_role_select → test_game_room_select_role, test_room_sea_select → test_game_room_select_sea, test_room_sig_select → test_game_room_select_sig, test_room_sky_select → test_game_room_select_sky, test_room_tray → test_game_room_tray, test_component_tray_tooltip → test_game_room_tray_tooltip; the post_page.py / room_page.py helper modules from the May-12 sprint absorbed the cross-file FT imports that would otherwise have cascade-broken on these renames
Some checks failed
ci/woodpecker/push/pyswiss Pipeline was successful
ci/woodpecker/push/main Pipeline failed

.woodpecker/main.yaml — CI test-FTs step splits into parallel siblings test-FTs-non-room (22 files via `ls functional_tests/test_*.py | grep -v 'test_game_room_'`) + test-FTs-room (9 files via `ls functional_tests/test_game_room_*.py`); room cluster is the heaviest (~70% of the pre-split ~40-min wall-clock) and now runs concurrently w. the rest instead of in series; DAG explicit via depends_on on every step (Woodpecker mixes default-sequential w. depends_on awkwardly, so each step pins its prerequisite); collectstatic stays in test-two-browser-FTs only — the shared workspace propagates assets to both parallel FT steps, no race + no duplication; screendumps + build-and-push fan back in (depends_on both parallel steps); deploy-staging + deploy-prod depend on build-and-push

smoke-import: 31/31 FT modules green after the rename pass

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-05-12 20:06:25 -04:00
parent af1a90e76b
commit f9c05a3eba
27 changed files with 713 additions and 625 deletions

View File

@@ -0,0 +1,198 @@
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
class WalletDisplayTest(FunctionalTest):
def setUp(self):
super().setUp()
Applet.objects.get_or_create(slug="wallet", defaults={"name": "Wallet"})
for slug, name, cols, rows in [
("wallet-balances", "Wallet Balances", 3, 3),
("wallet-tokens", "Wallet Tokens", 3, 3),
("wallet-payment", "Payment Methods", 6, 3),
]:
Applet.objects.get_or_create(
slug=slug,
defaults={"name": name, "grid_cols": cols, "grid_rows": rows, "context": "wallet"},
)
def test_new_user_wallet_shows_starting_balances(self):
# 1. Log in as new user
self.create_pre_authenticated_session("capman@test.io")
# 2. Navigate to dashboard
self.browser.get(self.live_server_url)
# 3. Find wallet applet summary card
self.wait_for(
lambda: self.browser.find_element(By.ID, "id_applet_wallet")
)
# 4. Click thru to full wallet page via manage link
self.browser.find_element(
By.CSS_SELECTOR, "#id_applet_wallet a.wallet-manage-link"
).click()
# 5. Assert user landed on wallet page
self.wait_for(
lambda: self.assertRegex(self.browser.current_url, r"/dashboard/wallet/$")
)
# 6. Assert writs balance shows 144 (complimentary bundle on signup)
self.wait_for(
lambda: self.assertEqual(
"144",
self.browser.find_element(By.ID, "id_writs_balance").text,
)
)
# 7. Assert esteem balance shows 0
self.assertEqual(
"0",
self.browser.find_element(By.ID, "id_esteem_balance").text,
)
# 8. Assert Coin-on-a-String token element present
coin = self.browser.find_element(By.ID, "id_coin_on_a_string")
# 9. Hover over it; assert tooltip appears w. name, entry text, 'no expiry'
ActionChains(self.browser).move_to_element(coin).perform()
self.wait_for(
lambda: self.assertTrue(
self.browser.find_element(By.ID, "id_tooltip_portal").is_displayed()
)
)
coin_tooltip = self.browser.find_element(By.ID, "id_tooltip_portal").text
self.assertIn("Coin-on-a-String", coin_tooltip)
self.assertIn("Admit 1 Entry", coin_tooltip)
self.assertIn("no expiry", coin_tooltip)
# 10. Assert ×1 Free Token present (complimentary on signup)
free_token = self.browser.find_element(By.ID, "id_free_token")
# 11. Hover over it; assert tooltip shows name, entry text, expiry date
ActionChains(self.browser).move_to_element(free_token).perform()
self.wait_for(
lambda: self.assertTrue(
self.browser.find_element(By.ID, "id_tooltip_portal").is_displayed()
)
)
free_tooltip = self.browser.find_element(By.ID, "id_tooltip_portal").text
self.assertIn("Free Token", free_tooltip)
self.assertIn("Admit 1 Entry", free_tooltip)
self.assertIn("Expires", free_tooltip)
def test_wallet_payment_section_renders(self):
# 1. Log in, navigate directly to wallet page
self.create_pre_authenticated_session("capman@test.io")
self.browser.get(self.live_server_url + "/dashboard/wallet/")
# 2. Assert saved payment methods section present
self.wait_for(
lambda: self.browser.find_element(By.ID, "id_payment_methods")
)
# 3. Assert the add-payment-method button is visible
self.browser.find_element(By.ID, "id_add_payment_method")
# 4. Assert Stripe Payment Element mount point exists
self.browser.find_element(By.ID, "id_stripe_payment_element")
def test_user_can_save_a_payment_method(self):
# 1. Log in, navigate to wallet page
self.create_pre_authenticated_session("capman@test.io")
self.browser.get(self.live_server_url + "/dashboard/wallet/")
# 2. Click add-payment-method btn
self.browser.find_element(By.ID, "id_add_payment_method").click()
# 3. Wait for Stripe Payment Element iframe to appear inside mount point
self.wait_for(
lambda: self.browser.find_element(
By.CSS_SELECTOR, "#id_stripe_payment_element iframe"
)
)
# 4. Switch into Stripe iframe to interact w. card fields
stripe_frame = self.browser.find_element(
By.CSS_SELECTOR, "#id_stripe_payment_element iframe"
)
self.browser.switch_to.frame(stripe_frame)
# 4a. Wait for card inputs to render inside iframe
self.wait_for(
lambda: self.browser.find_element(
By.CSS_SELECTOR, 'input[placeholder="1234 1234 1234 1234"]'
)
)
# 5. Fill in Stripe test card details
self.browser.find_element(
By.CSS_SELECTOR, 'input[placeholder="1234 1234 1234 1234"]'
).send_keys("4242424242424242")
self.browser.find_element(
By.CSS_SELECTOR, 'input[placeholder="MM / YY"]'
).send_keys("12 / 26")
self.browser.find_element(
By.CSS_SELECTOR, 'input[placeholder="CVC"]'
).send_keys("424")
self.browser.find_element(
By.CSS_SELECTOR, 'input[placeholder="12345"]'
).send_keys("42424")
# 6. Return to main doc & submit form
self.browser.switch_to.default_content()
self.wait_for(
lambda: self.assertFalse(
self.browser.find_element(By.ID, "id_save_payment_method").get_attribute("hidden")
)
)
self.browser.find_element(By.ID, "id_save_payment_method").click()
# 7. Wait for saved card to appear in payment methods list
# Assert last 4 digits shown (Stripe confirmSetup + server round-trip can be slow)
self.wait_for_slow(
lambda: self.assertIn(
"4242",
self.browser.find_element(By.ID, "id_payment_methods").text,
)
)
def test_user_can_cancel_adding_payment_method(self):
# 1. Log in, navigate to wallet page
self.create_pre_authenticated_session("capman@test.io")
self.browser.get(self.live_server_url + "/dashboard/wallet/")
# 2. Click Add Payment Method
self.wait_for_slow(
lambda: self.browser.find_element(By.ID, "id_add_payment_method")
).click()
# 3. Wait for Cancel button to appear (visible after setup-intent fetch returns)
self.wait_for_slow(
lambda: self.assertFalse(
self.browser.find_element(By.ID, "id_cancel_payment_method").get_attribute("hidden")
)
)
# 3a. Assert applet expanded to 15 rows
rows = self.browser.execute_script(
"return document.getElementById('id_payment_methods')"
".style.getPropertyValue('--applet-rows').trim()"
)
self.assertEqual(rows, '15')
# 4. Click Cancel
self.browser.find_element(By.ID, "id_cancel_payment_method").click()
# 5. Assert Cancel + Save buttons are hidden again
self.wait_for_slow(
lambda: self.assertTrue(
self.browser.find_element(By.ID, "id_cancel_payment_method").get_attribute("hidden")
)
)
self.assertTrue(
self.browser.find_element(By.ID, "id_save_payment_method").get_attribute("hidden")
)
# 6. Assert applet collapses back to 3 grid rows
rows = self.browser.execute_script(
"return document.getElementById('id_payment_methods')"
".style.getPropertyValue('--applet-rows').trim()"
)
self.assertEqual(rows, '3')
def test_user_can_purchase_tithe_token_bundle(self):
# 1. Log in, navigate to wallet page
self.create_pre_authenticated_session("capman@test.io")
self.browser.get(self.live_server_url + "/dashboard/wallet/")
# 2. Assert Tithe Token purchase section present
self.wait_for(
lambda: self.browser.find_element(By.ID, "id_tithe_token_shop")
)
# 3. Assert min. +1 bundle option is visible
bundle = self.browser.find_element(
By.CSS_SELECTOR, "#id_tithe_token_shop .token-bundle"
)
# 4. Assert ea. bundle shows token count & writ bonus placeholder
self.assertIn("Tithe Token", bundle.text)
self.assertIn("Writ", bundle.text)
# 5. (Placeholder) Purchase flow via Stripe not driven in this FT:
# Full charge assertion deferred until Stripe webhook handling implemented