Problem
Base.click_element waits for EC.element_to_be_clickable and then clicks:
def click_element(self, locator):
el = self._wait_clickable(locator)
el.click()
element_to_be_clickable checks that the element is visible and enabled, not that it is unobscured. The app renders Bootstrap toasts into a fixed #toasts-container after saves, initialised as new bootstrap.Toast(toastEl) with no options, so they inherit the defaults autohide: true, delay: 5000 and float over the page for five seconds. Any click landing in that window passes the wait and then throws:
selenium.common.exceptions.ElementClickInterceptedException: Element
<button class="btn p-0 border-0 text-secondary" type="button"> is not clickable
at point (1297,640) because another element <div class="toast-body"> obscures it
There is no toast handling anywhere in the integration suite: git grep -i toast experimenter/tests/integration returns nothing. click_element is the shared click path for the whole suite, so this is not specific to one test.
Observed on the Desktop Nimbus UI (Release Firefox) job taking a retry attempt on test_rollouts_ui.py::test_rollout_can_be_launched[FIREFOX_DESKTOP], raised from pages/base.py click_element via pages/rollouts/form_base.py edit. The earlier CI profiling work counted 39 of these in a single bad run.
Proposed change
In experimenter/tests/integration/nimbus/pages/base.py, retry the click when it is intercepted, and dismiss any visible toast before retrying so the retry does not have to wait out the full 5 second autohide:
- add
ElementClickInterceptedException to the selenium.common.exceptions import
- add a
_dismiss_toasts helper that removes the show class from .toast.show via JS, wrapped in contextlib.suppress (already imported)
- rewrite
click_element to attempt the click inside a short bounded WebDriverWait, dismissing toasts and returning False on ElementClickInterceptedException
The dismissal only runs after a real interception, so it never speculatively mutates page state, and it only touches .toast. No test asserts on toasts, so nothing loses coverage. A permanently obscured element still fails once the bounded wait expires, so this does not mask a genuine regression where something legitimately covers a control.
Out of scope
There are roughly 17 raw .click() call sites in page objects that bypass click_element. Those keep the current behaviour. This change covers the shared path, which is where the failure has actually been observed.
Acceptance criteria
- An intercepted click retries rather than failing immediately, bounded so a permanent overlay still fails.
- Visible toasts are dismissed only on the retry path, never pre-emptively.
Desktop Nimbus UI stops failing with ElementClickInterceptedException naming toast-body, confirmed by sampling job logs on main after merge.
┆Issue is synchronized with this Jira Task
Problem
Base.click_elementwaits forEC.element_to_be_clickableand then clicks:element_to_be_clickablechecks that the element is visible and enabled, not that it is unobscured. The app renders Bootstrap toasts into a fixed#toasts-containerafter saves, initialised asnew bootstrap.Toast(toastEl)with no options, so they inherit the defaultsautohide: true, delay: 5000and float over the page for five seconds. Any click landing in that window passes the wait and then throws:There is no toast handling anywhere in the integration suite:
git grep -i toast experimenter/tests/integrationreturns nothing.click_elementis the shared click path for the whole suite, so this is not specific to one test.Observed on the
Desktop Nimbus UI (Release Firefox)job taking a retry attempt ontest_rollouts_ui.py::test_rollout_can_be_launched[FIREFOX_DESKTOP], raised frompages/base.pyclick_elementviapages/rollouts/form_base.pyedit. The earlier CI profiling work counted 39 of these in a single bad run.Proposed change
In
experimenter/tests/integration/nimbus/pages/base.py, retry the click when it is intercepted, and dismiss any visible toast before retrying so the retry does not have to wait out the full 5 second autohide:ElementClickInterceptedExceptionto theselenium.common.exceptionsimport_dismiss_toastshelper that removes theshowclass from.toast.showvia JS, wrapped incontextlib.suppress(already imported)click_elementto attempt the click inside a short boundedWebDriverWait, dismissing toasts and returningFalseonElementClickInterceptedExceptionThe dismissal only runs after a real interception, so it never speculatively mutates page state, and it only touches
.toast. No test asserts on toasts, so nothing loses coverage. A permanently obscured element still fails once the bounded wait expires, so this does not mask a genuine regression where something legitimately covers a control.Out of scope
There are roughly 17 raw
.click()call sites in page objects that bypassclick_element. Those keep the current behaviour. This change covers the shared path, which is where the failure has actually been observed.Acceptance criteria
Desktop Nimbus UIstops failing withElementClickInterceptedExceptionnamingtoast-body, confirmed by sampling job logs onmainafter merge.┆Issue is synchronized with this Jira Task