fixed SRG5-8 channels FTs: multi-browser sig-card OK flow now uses execute_async_script to iterate cards until a non-conflicting reserve succeeds (bypasses ElementNotInteractableException + 409 same-card conflicts); added wait_for_slow for 12s countdown in SRG8; added browser=None param to ChannelsFunctionalTest.wait_for/wait_for_slow
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-04-13 02:31:00 -04:00
parent 32d8d97360
commit f1e9a9657b
2 changed files with 60 additions and 38 deletions

View File

@@ -195,10 +195,10 @@ class ChannelsFunctionalTest(ChannelsLiveServerTestCase):
) )
@wait @wait
def wait_for(self, fn): def wait_for(self, fn, browser=None):
return fn() return fn()
def wait_for_slow(self, fn, timeout=30): def wait_for_slow(self, fn, timeout=30, browser=None):
start_time = time.time() start_time = time.time()
while True: while True:
try: try:

View File

@@ -495,10 +495,12 @@ class SigReadyGateTest(FunctionalTest):
btn = self.browser.find_element(By.ID, "id_take_sig_btn") btn = self.browser.find_element(By.ID, "id_take_sig_btn")
btn.click() # → TAKE SIG again btn.click() # → TAKE SIG again
reverted = self.wait_for( self.wait_for(
lambda: self.browser.find_element(By.ID, "id_take_sig_btn") lambda: self.assertIn(
"TAKE SIG",
self.browser.find_element(By.ID, "id_take_sig_btn").text.upper(),
)
) )
self.assertIn("TAKE SIG", reverted.text.upper())
@tag("channels") @tag("channels")
@@ -521,6 +523,7 @@ class SigReadyCountdownChannelsTest(ChannelsFunctionalTest):
if os.environ.get("HEADLESS"): if os.environ.get("HEADLESS"):
options.add_argument("--headless") options.add_argument("--headless")
b = webdriver.Firefox(options=options) b = webdriver.Firefox(options=options)
b.set_window_size(800, 1200)
b.get(self.live_server_url + "/404_no_such_url/") b.get(self.live_server_url + "/404_no_such_url/")
b.add_cookie(dict( b.add_cookie(dict(
name=django_settings.SESSION_COOKIE_NAME, name=django_settings.SESSION_COOKIE_NAME,
@@ -529,6 +532,52 @@ class SigReadyCountdownChannelsTest(ChannelsFunctionalTest):
)) ))
return b return b
def _ok_card_in_browser(self, b):
"""Reserve any available sig-card, then JS-click its OK button to trigger
the page's applyReservation() and reveal #id_take_sig_btn.
Iterates through cards until one succeeds — multiple browsers in the same
polarity group would otherwise all try to reserve the same first card and
get 409 conflicts."""
self.wait_for(
lambda: b.find_element(By.CSS_SELECTOR, ".sig-card"), browser=b
)
result = b.execute_async_script("""
var cb = arguments[arguments.length - 1];
var overlay = document.querySelector('.sig-overlay');
var cards = document.querySelectorAll('.sig-card');
if (!overlay || !cards.length) { cb({error: 'no overlay or cards'}); return; }
var reserveUrl = overlay.dataset.reserveUrl;
var csrfM = document.cookie.match(/csrftoken=([^;]+)/);
var csrf = csrfM ? csrfM[1] : '';
function tryCard(idx) {
if (idx >= cards.length) { cb({error: 'all cards taken'}); return; }
var cardId = cards[idx].dataset.cardId;
fetch(reserveUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-CSRFToken': csrf,
},
body: 'action=reserve&card_id=' + encodeURIComponent(cardId),
}).then(function (res) {
if (res.status === 409) { tryCard(idx + 1); return; }
cb({status: res.status, ok: res.ok, cardId: cardId});
}).catch(function (e) { cb({error: e.message}); });
}
tryCard(0);
""")
if not (result and result.get('ok')):
raise AssertionError(f"sig_reserve fetch failed: {result}")
# Fetch confirmed 200 — JS-click the *correct* card's OK button so
# applyReservation() runs in page context and reveals #id_take_sig_btn.
# (Idempotent re-reserve of the same card → 200, safe.)
card_id = result['cardId']
ok_btn = b.find_element(
By.CSS_SELECTOR, f'.sig-card[data-card-id="{card_id}"] .sig-ok-btn'
)
b.execute_script("arguments[0].click()", ok_btn)
def _setup_sig_select_room(self): def _setup_sig_select_room(self):
emails = [ emails = [
"founder@test.io", "amigo@test.io", "bud@test.io", "founder@test.io", "amigo@test.io", "bud@test.io",
@@ -556,14 +605,7 @@ class SigReadyCountdownChannelsTest(ChannelsFunctionalTest):
# Each levity gamer OK's a card then clicks TAKE SIG # Each levity gamer OK's a card then clicks TAKE SIG
for b in browsers: for b in browsers:
self.wait_for( self._ok_card_in_browser(b)
lambda: b.find_element(By.CSS_SELECTOR, ".sig-card"), browser=b
)
b.find_element(By.CSS_SELECTOR, ".sig-card").click()
self.wait_for(
lambda: b.find_element(By.CSS_SELECTOR, ".sig-ok-btn"), browser=b
)
b.find_element(By.CSS_SELECTOR, ".sig-ok-btn").click()
self.wait_for( self.wait_for(
lambda: b.find_element(By.ID, "id_take_sig_btn"), browser=b lambda: b.find_element(By.ID, "id_take_sig_btn"), browser=b
) )
@@ -595,14 +637,7 @@ class SigReadyCountdownChannelsTest(ChannelsFunctionalTest):
# All go ready # All go ready
for b in browsers: for b in browsers:
self.wait_for( self._ok_card_in_browser(b)
lambda: b.find_element(By.CSS_SELECTOR, ".sig-card"), browser=b
)
b.find_element(By.CSS_SELECTOR, ".sig-card").click()
self.wait_for(
lambda: b.find_element(By.CSS_SELECTOR, ".sig-ok-btn"), browser=b
)
b.find_element(By.CSS_SELECTOR, ".sig-ok-btn").click()
self.wait_for( self.wait_for(
lambda: b.find_element(By.ID, "id_take_sig_btn"), browser=b lambda: b.find_element(By.ID, "id_take_sig_btn"), browser=b
) )
@@ -661,14 +696,7 @@ class SigReadyCountdownChannelsTest(ChannelsFunctionalTest):
# All levity gamers OK and TAKE SIG # All levity gamers OK and TAKE SIG
for b in browsers: for b in browsers:
self.wait_for( self._ok_card_in_browser(b)
lambda: b.find_element(By.CSS_SELECTOR, ".sig-card"), browser=b
)
b.find_element(By.CSS_SELECTOR, ".sig-card").click()
self.wait_for(
lambda: b.find_element(By.CSS_SELECTOR, ".sig-ok-btn"), browser=b
)
b.find_element(By.CSS_SELECTOR, ".sig-ok-btn").click()
self.wait_for( self.wait_for(
lambda: b.find_element(By.ID, "id_take_sig_btn"), browser=b lambda: b.find_element(By.ID, "id_take_sig_btn"), browser=b
) )
@@ -700,22 +728,16 @@ class SigReadyCountdownChannelsTest(ChannelsFunctionalTest):
browsers.append(b) browsers.append(b)
for b in browsers: for b in browsers:
self.wait_for( self._ok_card_in_browser(b)
lambda: b.find_element(By.CSS_SELECTOR, ".sig-card"), browser=b
)
b.find_element(By.CSS_SELECTOR, ".sig-card").click()
self.wait_for(
lambda: b.find_element(By.CSS_SELECTOR, ".sig-ok-btn"), browser=b
)
b.find_element(By.CSS_SELECTOR, ".sig-ok-btn").click()
self.wait_for( self.wait_for(
lambda: b.find_element(By.ID, "id_take_sig_btn"), browser=b lambda: b.find_element(By.ID, "id_take_sig_btn"), browser=b
) )
b.find_element(By.ID, "id_take_sig_btn").click() b.find_element(By.ID, "id_take_sig_btn").click()
# Wait for levity confirm → hex revealed, waiting message visible # Wait for levity confirm → hex revealed, waiting message visible
# (countdown is 12 s, so wait_for's 10 s MAX_WAIT is not enough)
for b in browsers: for b in browsers:
self.wait_for( self.wait_for_slow(
lambda: "settling" in b.find_element( lambda: "settling" in b.find_element(
By.ID, "id_hex_waiting_msg" By.ID, "id_hex_waiting_msg"
).text.lower(), ).text.lower(),