"""通用 App Page Object 基类。""" from datetime import datetime from pathlib import Path from appium.webdriver.common.appiumby import AppiumBy from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC SCREENSHOTS_DIR = Path("output/screenshots") class AppBasePage: """App Page Object 基类 — 各端通用。""" def __init__(self, driver, platform: str, timeout_ms: int = 30_000): self.driver = driver self.platform = platform # "android" | "ios" self.timeout = timeout_ms / 1000 def wait_visible_by_id(self, accessibility_id: str, timeout_s: float | None = None) -> None: wait = WebDriverWait(self.driver, timeout=timeout_s or self.timeout) wait.until( EC.visibility_of_element_located((AppiumBy.ACCESSIBILITY_ID, accessibility_id)) ) def wait_visible_by_text(self, text: str, timeout_s: float | None = None) -> None: wait = WebDriverWait(self.driver, timeout=timeout_s or self.timeout) locator = ( (AppiumBy.XPATH, f"//*[@text='{text}']") if self.platform == "android" else (AppiumBy.XPATH, f"//*[@label='{text}']") ) wait.until(EC.visibility_of_element_located(locator)) def tap_by_id(self, accessibility_id: str) -> None: self.wait_visible_by_id(accessibility_id) self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, accessibility_id).click() def tap_by_text(self, text: str) -> None: strategy = AppiumBy.XPATH locator = ( f"//*[@text='{text}']" if self.platform == "android" else f"//*[@label='{text}']" ) wait = WebDriverWait(self.driver, timeout=self.timeout) wait.until(EC.visibility_of_element_located((strategy, locator))) self.driver.find_element(strategy, locator).click() def fill_by_id(self, accessibility_id: str, value: str) -> None: self.wait_visible_by_id(accessibility_id) element = self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, accessibility_id) element.clear() element.send_keys(value) def scroll_down(self) -> None: size = self.driver.get_window_size() self.driver.swipe( start_x=size["width"] // 2, start_y=int(size["height"] * 0.7), end_x=size["width"] // 2, end_y=int(size["height"] * 0.3), duration=500, ) def screenshot(self, name: str) -> str: scenario_dir = SCREENSHOTS_DIR / self.__class__.__name__ scenario_dir.mkdir(parents=True, exist_ok=True) ts = datetime.now().strftime("%Y%m%d_%H%M%S") path = str(scenario_dir / f"{name}_{self.platform}_{ts}.png") self.driver.save_screenshot(path) return path