[Python] Selenium automation script

Discussão em 'Python' iniciado por Stack, Dezembro 30, 2024.

  1. Stack

    Stack Membro Participativo

    I'm building a script that automates going through a survey for work. I build surveys and I'm trying to create a script that checks that the surveys I build are working. Everything is fine up to a point where it simply crashes. I can't even tell from where. I keep having different errors occurring. The most recent is running into a stale error when working with multi_grid questions. I suspect it relates to the dom refreshing.

    The script creates a question type dictionary and if it runs into any of the dictionary questions it runs a sequence of entries and clicks.

    My issue is occuring when running into multi_grid questions where the element is not being clicked and it crashes. The process is the same as single_grid, but it will select more than one random element.

    def start_testing():
    try:
    str_base_url = entry_url.get()
    int_num_tests = int(entry_tests.get())

    if not str_base_url or int_num_tests <= 0:
    messagebox.showerror("Error", "Please enter both a valid URL and a positive
    number of respondents.")
    return

    driver = webdriver.Chrome()
    action = ActionChains(driver)

    for i in range(1, int_num_tests + 1):
    str_test_url = f"{str_base_url}&altid=myj_trial_27_{i}"
    driver.get(str_test_url)

    # Switch to the first iframe (index 0)
    try:
    driver.switch_to.frame(0) # Switch to the first iframe by index
    except NoSuchFrameException:
    print("Survey iframe not found.")
    continue

    while True:
    # Check if last page (completion page) is reached
    try:
    WebDriverWait(driver, 3).until(
    EC.presence_of_element_located((By.CLASS_NAME, "lastPageText"))
    )
    print(f"Test {i}/{int_num_tests} complete.")
    break # End the test for this respondent
    except TimeoutException:
    pass # Continue if last page is not reached

    # Locate question elements inside the iframe
    lst_cCheck = driver.find_elements(By.CLASS_NAME, "cCheck")
    lst_cRadio = driver.find_elements(By.CLASS_NAME, "cRadio")
    lst_cTextInput = driver.find_elements(By.CLASS_NAME, "cTextInput")
    lst_cTextArea = driver.find_elements(By.CLASS_NAME, "cTextArea")
    lst_cFInput = driver.find_elements(By.CLASS_NAME, "cFInput")
    lst_cBtnSingleGrid = driver.find_elements(By.XPATH, "//div[@id='SingleGridQ_btn']/div/div/div/p")
    lst_cBtnMultiGrid = driver.find_elements(By.XPATH, "//div[@id='MultiGridQ_btn']/div/div/p")
    lst_cSay = driver.find_elements(By.CLASS_NAME, "cSay")
    lst_dropdowns = driver.find_elements(By.ID, "n0A") # Locate dropdowns

    # Store non-empty question types
    dict_question_types = {
    "multi_select": lst_cCheck,
    "single_select": lst_cRadio,
    "short_text": lst_cTextInput,
    "long_text": lst_cTextArea,
    "numeric_input": lst_cFInput,
    "single_select_grid": lst_cBtnSingleGrid,
    "dropdown": lst_dropdowns,
    "multi_select_grid": lst_cBtnMultiGrid
    }
    dict_question_types = {k: v for k, v in dict_question_types.items() if v}

    # Interact with question types if they exist
    if dict_question_types:
    str_selected_type = random.choice(list(dict_question_types.keys()))
    obj_elements = dict_question_types[str_selected_type]

    if str_selected_type == "multi_select":
    # Randomly select up to the total number of checkboxes
    int_num_checks = random.randint(1, len(obj_elements))
    for obj_checkbox in random.sample(obj_elements, int_num_checks):
    action.click(obj_checkbox).perform()

    elif str_selected_type == "single_select":
    obj_random_radio = random.choice(obj_elements)
    action.click(obj_random_radio).perform()

    elif str_selected_type == "short_text":
    obj_text_input = random.choice(obj_elements)
    driver.execute_script("arguments[0].value = arguments[1];", obj_text_input, str_short_text)

    elif str_selected_type == "long_text":
    obj_text_area = random.choice(obj_elements)
    driver.execute_script("arguments[0].value = arguments[1];", obj_text_area, str_long_text)

    elif str_selected_type == "numeric_input":
    for obj_numeric_input in obj_elements:
    random_number = random.randint(1, 10)
    driver.execute_script("arguments[0].value = arguments[1];", obj_numeric_input, random_number)

    elif str_selected_type == "single_select_grid":
    # Keep selecting and clicking .cBtnSingleGrid until there are no more available
    while obj_elements:
    obj_grid_option = random.choice(obj_elements)
    driver.execute_script("arguments[0].click();", obj_grid_option)
    # Refresh the list of grid options after clicking
    obj_elements = driver.find_elements(By.XPATH, "//div[@id='SingleGridQ_btn']/div/div/div/p")

    print("All single grid options have been selected.")

    elif str_selected_type == "multi_select_grid":
    # Randomly select multiple grid options
    while obj_elements:
    int_num_checks = random.randint(1, len(obj_elements))
    selected_options = random.sample(obj_elements, int_num_checks)
    for obj_grid_option in selected_options:
    driver.execute_script("arguments[0].click();", obj_grid_option)
    obj_elements = driver.find_elements(By.XPATH, "//div[@id='MultiGridQ_btn']/div/div/p")

    # Click the Next button after each selection set
    try:
    btn_next = WebDriverWait(driver, 5).until(
    EC.element_to_be_clickable((By.ID, "btnNext"))
    )
    action.click(btn_next).perform()
    except TimeoutException:
    print("Next button not found or not clickable.")
    break
    # Refresh the list of grid options after clicking Next
    obj_elements = driver.find_elements(By.XPATH, "//div[@id='MultiGridQ_btn']/div/div/p")

    print("All multi-select grid options have been selected.")

    elif str_selected_type == "dropdown":
    for dropdown in obj_elements:
    # Click the dropdown to expand it
    dropdown.click()
    # Wait for options to be available
    time.sleep(0.5) # Optional delay to allow options to load
    # Locate the options within the dropdown
    options = dropdown.find_elements(By.TAG_NAME, "option")
    # Filter out options with a value of -1
    valid_options = [option for option in options if option.get_attribute("value") != "-1"]
    if valid_options:
    # Select a random option from the filtered list
    random_option = random.choice(valid_options)
    random_option.click()

    # After processing the question types, click the Next button
    try:
    if str_selected_type != "single_select_grid" and str_selected_type != "multi_select_grid":
    btn_next = WebDriverWait(driver, 5).until(
    EC.element_to_be_clickable((By.ID, "btnNext"))
    )
    action.click(btn_next).perform()
    continue # Move to the next page
    except TimeoutException:
    print("Next button not found or not clickable.")
    break

    # If no question types are available but .cSay exists, click 'Next'
    elif lst_cSay:
    try:
    btn_next = WebDriverWait(driver, 5).until(
    EC.element_to_be_clickable((By.ID, "btnNext"))
    )
    action.click(btn_next).perform()
    continue # Move to the next page
    except TimeoutException:
    print("Next button not found or not clickable.")
    break

    # If neither question types nor .cSay are present, break out
    else:
    print("No actionable elements found.")
    break


    except Exception as e:
    print(f"An error occurred: {e}")
    finally:
    driver.quit()
    messagebox.showinfo("Completed", f"Tested {int_num_tests} survey links.")
    root.destroy()

    Continue reading...

Compartilhe esta Página