Search code examples
pythonselenium-webdriverxpath

Selecting checkboxes that are wrapped with <a> and <i> tags?


I'm trying to check if the checkbox is selected using is_selected() method. I understand that since the element I am trying to validate is not a legit checkbox, it's not right to validate it this way. It's pretty obvious because it's wrapped in an anchor(<a>) and italic tag(<i>). I've tried different variants of XPaths such as:

//input[@name='non_stop']/preceding-sibling::i
//input[@name='non_stop']/parent::a
//a[@title='Non Stop Flights']/i[contains(@class, 'ico ico-checkbox')] (possibly the most wrong one here)

But is there a workaround to verify this? Here's my code:

driver = webdriver.Chrome()
driver.get("https://www.yatra.com/")
non_stop_flights = driver.find_element(
    By.XPATH, "//input[@name='non_stop']/preceding-sibling::i")
print("Before clicking")
print(non_stop_flights.is_selected())  # Expecting False here
non_stop_flights.click()
sleep(3)
print("After clicking")
print(non_stop_flights.is_selected())  # Expecting True here

But I am still getting False as the output in the last line. How to make it recognize that the checkbox has been checked?


Solution

  • The problem is that the non-stop INPUT isn't actually storing checked state. It's being stored in the I tag right above it but inside the A tag.

    <a for="BE_flight_non_stop" title="Non Stop Flights" class="custom-check">
    ->  <i class="ico ico-checkbox"></i>
        <input data-trackcategory="Home Page" data-trackaction="Booking Engine" data-trackvalue="Non Stop Flight - Checked/Unchecked" type="checkbox" name="non_stop" id="BE_flight_non_stop" class="eventTrackable js-prodSpecEvtCat">
        Non Stop Flights
    </a>
    

    The checked state is tracked using a class on the I tag...

    Not checked

    <i class="ico ico-checkbox"></i>
    

    Checked

    <i class="ico ico-checkbox ico-checkbox-checked"></i>
                               ^^^^^^^^^^^^^^^^^^^^ checked state
    

    With this info, we can make a quick method to determine the checked state of these fields

    def is_checked(e):
        """
        Returns a True if the checkbox is checked, False otherwise.
        
        Parameters:
            e (WebElement): The I tag for the checkbox
        """
        return "ico-checkbox-checked" in e.get_attribute("class")
    

    and then call it from your original (modified) script

    driver = webdriver.Chrome()
    driver.get("https://www.yatra.com/")
    non_stop_flights = driver.find_element(By.CSS_SELECTOR, "a[title='Non Stop Flights'] i")
    print("Before clicking")
    print(is_checked(non_stop_flights))  # Expecting False here
    non_stop_flights.click()
    print("After clicking")
    print(is_checked(non_stop_flights))  # Expecting True here
    

    and it works as expected.