2025, Sep 23 11:00

How to stop FlixBus origin and destination fields resetting in Selenium/Python: handle the shadow DOM cookie banner and confirm autocomplete

Troubleshoot FlixBus form resets in Selenium/Python: accept the shadow DOM cookie banner, wait for inputs, and select autocomplete options for stable runs.

Automating form filling on modern websites can be deceptive: you type into one field, switch to another, and the first one silently resets. That’s exactly what happens on flixbus.ca when you try to change the default route using Selenium/Python. The origin switches from Toronto to Ottawa just fine, but as soon as you update the destination to Sudbury or touch the date, the origin flips back. The root cause isn’t in your send_keys calls at all.

Reproducing the issue

The following script opens flixbus.ca, tries to accept cookies, and then overwrites the From, To and date fields. On interaction with Destination or Date, the Origin input reverts to the default value.

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.keys import Keys
import time

flix_url = 'https://www.flixbus.ca/'

from_value = 'Ottawa (VIA Rail)'
to_value = 'Sudbury, ON'
when_value = 'Sat, Sep 20'

browser = webdriver.Chrome()
browser.maximize_window()
browser.get(flix_url)

# attempt to accept cookies
action_cookie = browser.find_element(By.XPATH, '//*[@id="uc-center     container"]/div[2]/div/div/div/button[2]')
action_cookie.click()

# set origin
from_input = browser.find_element(By.ID, 'searchInput-from')
from_input.clear()
time.sleep(1)
from_input.send_keys(from_value)
time.sleep(1)

# set destination
to_input = browser.find_element(By.ID, 'searchInput-to')
to_input.clear()
time.sleep(1)
to_input.send_keys(to_value)
time.sleep(1)

# set date
date_input = browser.find_element(By.ID, 'dateInput-from')
date_input.click()
time.sleep(1)
date_input.send_keys(when_value)

# submit search
submit_btn = browser.find_element(By.XPATH, '//*[@id="search-mask-    component"]/div/div/div/div/div[5]/div/button')
submit_btn.click()

What’s actually going on

The cookie banner on this site is rendered inside a shadow DOM. The Accept All control isn’t part of the regular DOM tree, so a direct find-and-click via By.XPATH is unreliable. That click is flaky, and as a result the subsequent Javascript/AJAX that controls the From/Origin and To/Destination widgets doesn’t stabilize. You end up typing into inputs that are later re-rendered by the page, wiping your changes.

Working approach

The fix is to interact with the cookie banner through its shadow root and only then proceed with the form. Use shadowRoot.querySelector to get the Accept All button, click it, and wait for the inputs to be truly clickable. Once typing is done, pick the intended option from the autocomplete list so the page acknowledges your selection.

from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
import time

browser = webdriver.Chrome()
browser.maximize_window()
browser.get("https://www.flixbus.ca/")

# accept cookies via shadow DOM
time.sleep(10)
accept_btn = browser.execute_script(
    """return document.querySelector('#usercentrics-root').shadowRoot
    .querySelector("button[data-testid='uc-accept-all-button']")"""
)
accept_btn.click()

# fill From and confirm via autocomplete
from_box = WebDriverWait(browser, 20).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, "input#searchInput-from"))
)
from_box.click()
from_box.clear()
from_box.send_keys("Ottawa (VIA Rail)")
WebDriverWait(browser, 20).until(
    EC.element_to_be_clickable((By.XPATH, "//div[@data-e2e='autocomplete-options-from']//span[contains(., 'Ottawa (VIA Rail']"))
).click()

# fill To and confirm via autocomplete
to_box = WebDriverWait(browser, 20).until(
    EC.element_to_be_clickable((By.CSS_SELECTOR, "input#searchInput-to"))
)
to_box.click()
to_box.clear()
to_box.send_keys("Sudbury, ON")
WebDriverWait(browser, 20).until(
    EC.element_to_be_clickable((By.XPATH, "//div[@data-e2e='autocomplete-options-to']//span[contains(., 'Sudbury, ON')]"))
).click()

Why this matters

Cookie consent layers increasingly live in shadow DOMs. If you ignore that and try to brute-force a click through the regular DOM, your test will behave inconsistently. For data extraction and E2E flows, this kind of flakiness leads to phantom regressions, heisenbugs, and hours lost on chasing non-deterministic state. Handling the consent banner correctly restores stability and keeps the form inputs from being reset by late-stage rendering.

Takeaways

Before interacting with key inputs, properly dismiss overlays built with shadow DOM using shadowRoot.querySelector. Combine that with explicit waits for clickability and select concrete entries from autocomplete suggestions. On pages like flixbus.ca this sequence prevents the Origin and Destination fields from reverting and makes your Selenium automation reliable.

The article is based on a question from StackOverflow by brooklin7 and an answer by undetected Selenium.