How to identify if Angular toggle-switch toggle is clicked?

Question:

I am trying to define if Angular’s toggle-switch is clicked or not using Selenium and Python tools.
When open a new form my input id has class ng-untouched ng-pristine ng-valid.

After clicked, it changes to ng-valid ng-dirty ng-touched. But after a third click nothing changes.
After I click it and save the form, the class stays always ng-valid ng-dirty ng-touched and remains the same after any change (even after disabling toggle and saving).
This is not a bug because changes are saved on server side.

Which CSS property should I look at to define what is changed?
Here is html code sample:

<div _ngcontent-c26="" class="form-group">
    <label _ngcontent-c26="" for="checked">Checked</label>
    <div _ngcontent-c26="" class="toggle-switch">
        <input _ngcontent-c26="" id="opened" name="checked" type="checkbox" class="ng-valid ng-dirty ng-touched">
            <label _ngcontent-c26="" for="checked"></label>
        </div>
        <span _ngcontent-c26="" class="form-control-helper"></span>
    </div>
Asked By: vitaliis

||

Answers:

Form State and Input State

AngularJS constantly updates the state of both the <form> and the <input> fields.

Input fields have the following states:

  • $untouched: The field has not been touched yet
  • $touched: The field has been touched
  • $pristine: The field has not been modified yet
  • $dirty: The field has been modified
  • $invalid: The field content is not valid
  • $valid: The field content is valid

These are the properties of the <input> field, and are either true or false.

Forms also have the following states:

  • $pristine: No fields have been modified yet
  • $dirty: One or more have been modified
  • $invalid: The form content is not valid
  • $valid: The form content is valid
  • $submitted: The form is submitted

This usecase

To validate the Angular toggle-switch state using Selenium and you have to induce WebDriverWait for visibility_of_element_located and you can use either of the following Locator Strategies:

  • Probing ng-touched:

    try:
        WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.XPATH, "//input[@id='opened' and @name='checked'][contains(@class, 'ng-touched')]")))
        print("Toggle Switch has been touched")
    except TimeoutException:
        print("Toggle Switch has not been touched")
    
  • Probing ng-dirty:

    try:
        WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.XPATH, "//input[@id='opened' and @name='checked'][contains(@class, 'ng-dirty')]")))
        print("Toggle Switch has been touched")
    except TimeoutException:
        print("Toggle Switch has not been touched")
    
  • Note : You have to add the following imports :

    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.common.by import By
    from selenium.webdriver.support import expected_conditions as EC
    
Answered By: undetected Selenium

After trying a few options and more research I found two solutions to test is a toggle/checkbox is checked.
First, by using .is_selected() Selenium’s method:

driver.find_element_by_css_selector("input[type=checkbox]").is_selected()

It returns True if a toggle is ON and False if it is OFF.

Second, I looked closely and CSS settings and found out that when a toggle is ON, :checked is added to it. So, you can use a boolean expression for this locator.
In Chrome’s DevTools this attribute can be found in Properties tab, the name is checked. More details are here:

The locator for checked toggle will look like:

input[type=checkbox]:checked

For unchecked:

input[type=checkbox]

Also, there is a third option which will work for many cases. For example, if a toggle changes its color, its property is also changed. So you can use:

driver.find_element_by_css_selector("input[type=checkbox]").value_of_css_property("background-color") == "some color"

For example, place "#fff" (white) if you expect this CSS property to be white.

Answered By: vitaliis