Initial commit
Dieser Commit ist enthalten in:
151
social_networks/gmail/gmail_ui_helper.py
Normale Datei
151
social_networks/gmail/gmail_ui_helper.py
Normale Datei
@ -0,0 +1,151 @@
|
||||
"""
|
||||
Gmail UI Helper - Hilfsfunktionen für UI-Interaktionen
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
import random
|
||||
import os
|
||||
from typing import Optional
|
||||
from playwright.sync_api import Page, ElementHandle
|
||||
|
||||
logger = logging.getLogger("gmail_ui_helper")
|
||||
|
||||
class GmailUIHelper:
|
||||
"""
|
||||
Hilfsklasse für Gmail UI-Interaktionen
|
||||
"""
|
||||
|
||||
def __init__(self, page: Page, screenshots_dir: str = None, save_screenshots: bool = True):
|
||||
"""
|
||||
Initialisiert den UI Helper
|
||||
"""
|
||||
self.page = page
|
||||
self.screenshots_dir = screenshots_dir or "logs/screenshots"
|
||||
self.save_screenshots = save_screenshots
|
||||
|
||||
# Screenshot-Verzeichnis erstellen falls nötig
|
||||
if self.save_screenshots and not os.path.exists(self.screenshots_dir):
|
||||
os.makedirs(self.screenshots_dir)
|
||||
|
||||
def take_screenshot(self, name: str) -> Optional[str]:
|
||||
"""
|
||||
Erstellt einen Screenshot
|
||||
"""
|
||||
if not self.save_screenshots:
|
||||
return None
|
||||
|
||||
try:
|
||||
timestamp = int(time.time())
|
||||
filename = f"{name}_{timestamp}.png"
|
||||
filepath = os.path.join(self.screenshots_dir, filename)
|
||||
self.page.screenshot(path=filepath)
|
||||
logger.debug(f"Screenshot gespeichert: {filepath}")
|
||||
return filepath
|
||||
except Exception as e:
|
||||
logger.warning(f"Fehler beim Erstellen des Screenshots: {e}")
|
||||
return None
|
||||
|
||||
def wait_for_element(self, selector: str, timeout: int = 30000) -> bool:
|
||||
"""
|
||||
Wartet auf ein Element
|
||||
"""
|
||||
try:
|
||||
self.page.wait_for_selector(selector, timeout=timeout)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Element {selector} nicht gefunden nach {timeout}ms")
|
||||
return False
|
||||
|
||||
def type_with_delay(self, selector: str, text: str, delay_min: float = 0.05, delay_max: float = 0.15):
|
||||
"""
|
||||
Tippt Text mit menschenähnlicher Verzögerung
|
||||
"""
|
||||
try:
|
||||
element = self.page.locator(selector)
|
||||
element.click()
|
||||
|
||||
for char in text:
|
||||
element.type(char)
|
||||
time.sleep(random.uniform(delay_min, delay_max))
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Tippen in {selector}: {e}")
|
||||
raise
|
||||
|
||||
def click_with_retry(self, selector: str, max_attempts: int = 3) -> bool:
|
||||
"""
|
||||
Klickt auf ein Element mit Wiederholungsversuchen
|
||||
"""
|
||||
for attempt in range(max_attempts):
|
||||
try:
|
||||
self.page.click(selector)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.warning(f"Klick-Versuch {attempt + 1} fehlgeschlagen: {e}")
|
||||
if attempt < max_attempts - 1:
|
||||
time.sleep(random.uniform(1, 2))
|
||||
|
||||
return False
|
||||
|
||||
def scroll_to_element(self, selector: str):
|
||||
"""
|
||||
Scrollt zu einem Element
|
||||
"""
|
||||
try:
|
||||
self.page.locator(selector).scroll_into_view_if_needed()
|
||||
time.sleep(random.uniform(0.5, 1))
|
||||
except Exception as e:
|
||||
logger.warning(f"Fehler beim Scrollen zu {selector}: {e}")
|
||||
|
||||
def is_element_visible(self, selector: str) -> bool:
|
||||
"""
|
||||
Prüft ob ein Element sichtbar ist
|
||||
"""
|
||||
try:
|
||||
return self.page.locator(selector).is_visible()
|
||||
except:
|
||||
return False
|
||||
|
||||
def get_element_text(self, selector: str) -> Optional[str]:
|
||||
"""
|
||||
Holt den Text eines Elements
|
||||
"""
|
||||
try:
|
||||
return self.page.locator(selector).text_content()
|
||||
except Exception as e:
|
||||
logger.warning(f"Fehler beim Lesen des Texts von {selector}: {e}")
|
||||
return None
|
||||
|
||||
def select_dropdown_option(self, selector: str, value: str):
|
||||
"""
|
||||
Wählt eine Option aus einem Dropdown
|
||||
"""
|
||||
try:
|
||||
self.page.select_option(selector, value)
|
||||
time.sleep(random.uniform(0.3, 0.6))
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Auswählen von {value} in {selector}: {e}")
|
||||
raise
|
||||
|
||||
def wait_for_navigation(self, timeout: int = 30000):
|
||||
"""
|
||||
Wartet auf Navigation
|
||||
"""
|
||||
try:
|
||||
self.page.wait_for_load_state("networkidle", timeout=timeout)
|
||||
except Exception as e:
|
||||
logger.warning(f"Navigation-Timeout nach {timeout}ms: {e}")
|
||||
|
||||
def wait_for_loading_to_finish(self):
|
||||
"""
|
||||
Wartet bis Ladeanimation verschwunden ist
|
||||
"""
|
||||
try:
|
||||
# Warte bis der Loading Spinner nicht mehr sichtbar ist
|
||||
from social_networks.gmail import gmail_selectors as selectors
|
||||
if self.is_element_visible(selectors.LOADING_SPINNER):
|
||||
self.page.wait_for_selector(selectors.LOADING_SPINNER, state="hidden", timeout=10000)
|
||||
time.sleep(random.uniform(0.5, 1))
|
||||
except:
|
||||
pass
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren