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
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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:
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
Reference in New Issue
Block a user