diff --git a/src/functional_tests/base.py b/src/functional_tests/base.py index b587d79..7d950a4 100644 --- a/src/functional_tests/base.py +++ b/src/functional_tests/base.py @@ -42,7 +42,17 @@ class FunctionalTest(StaticLiveServerTestCase): options = webdriver.FirefoxOptions() if os.environ.get("HEADLESS"): options.add_argument("--headless") - browser = webdriver.Firefox(options=options) + try: + browser = webdriver.Firefox(options=options) + except WebDriverException as e: + # Geckodriver spawn race under --parallel: another worker may be + # mid-spawn and the binary briefly reports "wrong permissions". + # One sleep+retry absorbs the race; a genuine install fault will + # fail both attempts. See pipeline #302/#303. + if "geckodriver" not in str(e): + raise + time.sleep(1) + browser = webdriver.Firefox(options=options) browser.set_window_size(width, height) return browser diff --git a/src/functional_tests/test_admin_tarot.py b/src/functional_tests/test_admin_tarot.py index 9dd3e37..b7e1d1b 100644 --- a/src/functional_tests/test_admin_tarot.py +++ b/src/functional_tests/test_admin_tarot.py @@ -55,6 +55,14 @@ class TarotAdminTest(FunctionalTest): self.browser.find_element(By.ID, "id_username").send_keys("admin@example.com") self.browser.find_element(By.ID, "id_password").send_keys("correct-password") self.browser.find_element(By.CSS_SELECTOR, "input[type=submit]").click() + # Wait for the POST → 302 → admin home to actually land before + # returning — otherwise the caller's `browser.get(...)` can race + # the in-flight form submit, hit /admin/login/?next=... and never + # recover. Same flake class as 054b0aa fixed in test_admin.py. + self.wait_for(lambda: self.assertIn( + "Site administration", + self.browser.find_element(By.TAG_NAME, "body").text, + )) # ------------------------------------------------------------------ # # Test 1a — admin home lists Tarot cards + Deck variants under Epic # diff --git a/src/functional_tests/test_jasmine.py b/src/functional_tests/test_jasmine.py index 43bc406..cf4d9e0 100644 --- a/src/functional_tests/test_jasmine.py +++ b/src/functional_tests/test_jasmine.py @@ -20,4 +20,11 @@ class JasmineTest(FunctionalTest): detail = "\n".join(f.text for f in failures) if failures else "(no detail)" self.fail(f"{result.text}\nFailing specs:\n{detail}") - self.wait_for(check_results) + # wait_for_slow w. 60s ceiling — the spec suite has grown well past the + # 10s MAX_WAIT the default wait_for affords. Under CI contention + # (parallel Selenium workers competing for CPU) Jasmine's still + # reporting "Running..." at 10s; pipeline #303 caught this. The + # check_results body keeps raising AssertionError ("Running..." doesn't + # match the 0-failures regex) so wait_for_slow naturally retries til + # the result settles or the timeout fires. + self.wait_for_slow(check_results, timeout=60)