#!/usr/bin/env python3 """ Autofill-Script fuer das IBO-Angular-Formular (ng-zorro / Formly). Fuellt alle Inputfelder mit Mock-Daten. Weil es ein Angular-Formular ist, reicht ein einfaches value-Setzen nicht -> es werden echte input/change-Events via Selenium ausgeloest, damit Angular die Werte uebernimmt. Setup: pip install selenium (Chrome + passender chromedriver muessen installiert sein; bei aktuellem Selenium 4 wird der Driver automatisch verwaltet.) Benutzung: python autofill.py "http://localhost:4200/dein/formular" # oder eine lokale Datei: python autofill.py "file:///Users/timu/programming/momentrum/projekte/ibo/index.html" """ import sys import time from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # --------------------------------------------------------------------------- # Felder werden aus config.json (application) gebaut. Siehe config.py. # Die formly-IDs stammen aus dem gerenderten Formular (data-cy / id). # --------------------------------------------------------------------------- import config as _config def _build_fields(app): t1, t2 = app["tenant1"], app["tenant2"] text_fields = { # Vorgang "formly_2_input_reserveNumber_0": app["reserveNumber"], "formly_2_input_validUntil_1": app["validUntil"], # Mieter 1 "formly_3_input_firstName_0": t1["firstName"], "formly_3_input_lastName_1": t1["lastName"], "formly_3_input_dateOfBirth_2": t1["dateOfBirth"], "formly_4_input_street_0": t1["street"], "formly_4_input_housenumber_1": t1["housenumber"], "formly_4_input_zipcode_2": t1["zipcode"], "formly_4_input_city_3": t1["city"], "formly_5_input_phoneNumberPrivate_0": t1["phonePrivate"], "formly_5_input_phoneNumberBusiness_1": t1["phoneBusiness"], # Mieter 2 "formly_6_input_firstName2_0": t2["firstName"], "formly_6_input_lastName2_1": t2["lastName"], "formly_6_input_dateOfBirth2_2": t2["dateOfBirth"], "formly_7_input_street2_0": t2["street"], "formly_7_input_housenumber2_1": t2["housenumber"], "formly_7_input_zipcode2_2": t2["zipcode"], "formly_7_input_city2_3": t2["city"], "formly_8_input_phoneNumberPrivate2_0": t2["phonePrivate"], "formly_8_input_phoneNumberBusiness2_1": t2["phoneBusiness"], } checkboxes = [] if app.get("secondTenant", True): checkboxes.append("formly_5_checkbox_secondTenant_5") if app.get("consent", True): checkboxes += ["formly_18_checkbox_dataCorrectness_20", "formly_18_checkbox_dataPrivacy_21"] selects = {"formly_2_select_numberRooms_2": str(app["numberRooms"])} return text_fields, checkboxes, selects TEXT_FIELDS, CHECKBOXES, SELECTS = _build_fields(_config.CFG["application"]) def reload_fields(): """Uebernimmt geaenderte config.json-Werte.""" global TEXT_FIELDS, CHECKBOXES, SELECTS TEXT_FIELDS, CHECKBOXES, SELECTS = _build_fields( _config.reload()["application"]) # --------------------------------------------------------------------------- # Fuell-Logik # --------------------------------------------------------------------------- def fill_text(driver, wait, field_id, value): """Fuellt ein Textfeld und loest Angular-Events aus.""" try: el = wait.until(EC.presence_of_element_located((By.ID, field_id))) driver.execute_script("arguments[0].scrollIntoView({block:'center'});", el) el.clear() el.click() el.send_keys(value) # blur -> Angular validiert / uebernimmt el.send_keys(Keys.TAB) print(f" [ok] {field_id} = {value}") except Exception as e: print(f" [skip] {field_id}: {e.__class__.__name__}") def set_checkbox(driver, field_id, checked=True): try: el = driver.find_element(By.ID, field_id) driver.execute_script("arguments[0].scrollIntoView({block:'center'});", el) if el.is_selected() != checked: driver.execute_script("arguments[0].click();", el) print(f" [ok] checkbox {field_id} -> {checked}") except Exception as e: print(f" [skip] checkbox {field_id}: {e.__class__.__name__}") def select_option(driver, wait, field_id, option_text): """Bedient ein @ng-select/ng-select Dropdown (nicht ng-zorro!). Struktur: Input role=combobox oeffnet ein .ng-dropdown-panel mit Optionen als
...
. """ try: trigger = wait.until(EC.element_to_be_clickable((By.ID, field_id))) driver.execute_script("arguments[0].scrollIntoView({block:'center'});", trigger) # Den umschliessenden .ng-select-Container klicken -> oeffnet Panel zuverlaessig container = driver.execute_script( "return arguments[0].closest('.ng-select');", trigger) or trigger container.click() time.sleep(0.4) # Passende Option im Dropdown-Panel finden (erst exakt, dann enthaelt) opt_xpath = ( "//div[contains(@class,'ng-option')]" f"[normalize-space()='{option_text}']" " | //div[contains(@class,'ng-option')]" f"[contains(normalize-space(),'{option_text}')]" ) option = wait.until(EC.element_to_be_clickable((By.XPATH, opt_xpath))) option.click() print(f" [ok] select {field_id} = {option_text}") except Exception as e: # Fallback: ng-select ist durchsuchbar -> tippen + Enter try: trigger.send_keys(option_text) time.sleep(0.4) trigger.send_keys(Keys.ENTER) print(f" [ok] select {field_id} = {option_text} (getippt)") except Exception: print(f" [skip] select {field_id}: {e.__class__.__name__}") def main(): url = sys.argv[1] if len(sys.argv) > 1 else \ "file:///Users/timu/programming/momentrum/projekte/ibo/index.html" options = webdriver.ChromeOptions() # options.add_argument("--headless=new") # bei Bedarf ohne sichtbares Fenster driver = webdriver.Chrome(options=options) wait = WebDriverWait(driver, 10) try: print(f"Oeffne {url}") driver.get(url) time.sleep(1.5) # Angular-Bootstrap abwarten # Erst zweiten Mieter aktivieren (schaltet dessen Felder frei) print("Checkbox 'zweiter Mieter':") set_checkbox(driver, "formly_5_checkbox_secondTenant_5", True) time.sleep(0.5) print("Dropdowns:") for fid, opt in SELECTS.items(): select_option(driver, wait, fid, opt) print("Textfelder:") for fid, val in TEXT_FIELDS.items(): fill_text(driver, wait, fid, val) print("Zustimmungs-Checkboxen:") for cid in CHECKBOXES: if cid == "formly_5_checkbox_secondTenant_5": continue # bereits gesetzt set_checkbox(driver, cid, True) print("\nFertig. Fenster bleibt offen. Programm laeuft weiter - " "mit Strg+C beenden (oder Browser-Fenster schliessen).") # Programm bewusst NICHT beenden -> Browser bleibt offen. while True: time.sleep(3600) except KeyboardInterrupt: print("\nBeende ...") driver.quit() if __name__ == "__main__": main()