diff --git a/tests/e2e/utils.py b/tests/e2e/utils.py index 5fab62ed18..d92896cfd5 100644 --- a/tests/e2e/utils.py +++ b/tests/e2e/utils.py @@ -249,36 +249,60 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase): Raises a clear test failure if the element isn't found, the text doesn't appear within `timeout` seconds, or the text is not valid JSON. """ + use_body = context is None + wait_timeout = timeout or self.wait_timeout + + def get_context() -> WebElement: + """Get or refresh the context element.""" + if use_body: + return self.driver.find_element(By.TAG_NAME, "body") + return context + + def get_text_safely() -> str: + """Get element text, re-finding element if stale.""" + for _ in range(5): + try: + return get_context().text.strip() + except StaleElementReferenceException: + sleep(0.5) + return get_context().text.strip() + + def get_inner_html_safely() -> str: + """Get innerHTML, re-finding element if stale.""" + for _ in range(5): + try: + return get_context().get_attribute("innerHTML") or "" + except StaleElementReferenceException: + sleep(0.5) + return get_context().get_attribute("innerHTML") or "" try: - if context is None: - context = self.driver.find_element(By.TAG_NAME, "body") + get_context() except NoSuchElementException: self.fail( f"No element found (defaulted to ). Current URL: {self.driver.current_url}" ) - wait_timeout = timeout or self.wait_timeout - wait = WebDriverWait(context, wait_timeout) + wait = WebDriverWait(self.driver, wait_timeout) try: - wait.until(lambda d: len(d.text.strip()) != 0) + wait.until(lambda d: len(get_text_safely()) != 0) except TimeoutException: - snippet = context.text.strip()[:500].replace("\n", " ") + snippet = get_text_safely()[:500].replace("\n", " ") self.fail( f"Timed out waiting for element text to appear at {self.driver.current_url}. " f"Current content: {snippet or ''}" ) - body_text = context.text.strip() - inner_html = context.get_attribute("innerHTML") or "" + body_text = get_text_safely() + inner_html = get_inner_html_safely() if "redirecting" in inner_html.lower(): try: - wait.until(lambda d: "redirecting" not in d.get_attribute("innerHTML").lower()) + wait.until(lambda d: "redirecting" not in get_inner_html_safely().lower()) except TimeoutException: - snippet = context.text.strip()[:500].replace("\n", " ") - inner_html = context.get_attribute("innerHTML") or "" + snippet = get_text_safely()[:500].replace("\n", " ") + inner_html = get_inner_html_safely() self.fail( f"Timed out waiting for redirect to finish at {self.driver.current_url}. " @@ -286,8 +310,8 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase): f"{inner_html or ''}" ) - inner_html = context.get_attribute("innerHTML") or "" - body_text = context.text.strip() + inner_html = get_inner_html_safely() + body_text = get_text_safely() snippet = body_text[:500].replace("\n", " ")