""" 基础页面类 所有页面对象的基类,提供通用方法。 """ from abc import ABC, abstractmethod from typing import Optional, List from playwright.sync_api import Page, Locator, expect class BasePage(ABC): """基础页面类""" def __init__(self, page: Page, base_url: str = ""): self.page = page self.base_url = base_url @abstractmethod def navigate(self, path: str = "") -> None: """导航到指定路径""" pass @abstractmethod def is_loaded(self) -> bool: """检查页面是否加载完成""" pass def wait_for_load(self, timeout: int = 30000) -> None: """等待页面加载完成""" self.page.wait_for_load_state("networkidle", timeout=timeout) def wait_for_selector( self, selector: str, timeout: int = 10000, state: str = "visible" ) -> Locator: """等待元素出现""" return self.page.wait_for_selector(selector, timeout=timeout, state=state) def click_element(self, selector: str, timeout: int = 10000) -> None: """点击元素""" self.wait_for_selector(selector, timeout=timeout).click() def fill_input(self, selector: str, value: str, timeout: int = 10000) -> None: """填充输入框""" element = self.wait_for_selector(selector, timeout=timeout) element.fill(value) def get_text(self, selector: str, timeout: int = 10000) -> str: """获取元素文本""" return ( self.wait_for_selector(selector, timeout=timeout).text_content() or "" ) def get_input_value(self, selector: str, timeout: int = 10000) -> str: """获取输入框值""" return ( self.wait_for_selector(selector, timeout=timeout).input_value() or "" ) def is_element_visible(self, selector: str, timeout: int = 5000) -> bool: """检查元素是否可见""" try: self.page.wait_for_selector( selector, timeout=timeout, state="visible" ) return True except: return False def is_element_enabled(self, selector: str, timeout: int = 5000) -> bool: """检查元素是否可用""" try: element = self.page.wait_for_selector( selector, timeout=timeout, state="visible" ) return element.is_enabled() except: return False def get_elements_count(self, selector: str) -> int: """获取元素数量""" return len(self.page.locator(selector).all()) def select_option(self, selector: str, value: str, timeout: int = 10000) -> None: """选择下拉选项""" self.wait_for_selector(selector, timeout=timeout).select_option(value) def check_checkbox(self, selector: str, timeout: int = 10000) -> None: """勾选复选框""" element = self.wait_for_selector(selector, timeout=timeout) if not element.is_checked(): element.check() def uncheck_checkbox(self, selector: str, timeout: int = 10000) -> None: """取消勾选复选框""" element = self.wait_for_selector(selector, timeout=timeout) if element.is_checked(): element.uncheck() def scroll_to_element(self, selector: str, timeout: int = 10000) -> None: """滚动到元素""" element = self.wait_for_selector(selector, timeout=timeout) element.scroll_into_view_if_needed() def take_screenshot(self, name: str, full_page: bool = False) -> str: """截图""" from pathlib import Path from datetime import datetime screenshot_dir = Path("reports/screenshots") screenshot_dir.mkdir(parents=True, exist_ok=True) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"{name}_{timestamp}.png" filepath = screenshot_dir / filename self.page.screenshot(path=str(filepath), full_page=full_page) return str(filepath) def get_current_url(self) -> str: """获取当前URL""" return self.page.url def go_back(self) -> None: """返回上一页""" self.page.go_back() def reload(self) -> None: """刷新页面""" self.page.reload() def wait_for_timeout(self, timeout: int) -> None: """等待指定时间(毫秒)""" self.page.wait_for_timeout(timeout) def assert_element_text( self, selector: str, expected_text: str, timeout: int = 10000 ) -> None: """断言元素文本""" element = self.wait_for_selector(selector, timeout=timeout) expect(element).to_have_text(expected_text) def assert_element_contains_text( self, selector: str, expected_text: str, timeout: int = 10000 ) -> None: """断言元素包含文本""" element = self.wait_for_selector(selector, timeout=timeout) expect(element).to_contain_text(expected_text) def assert_url_contains(self, text: str) -> None: """断言URL包含文本""" expect(self.page).to_have_url(lambda url: text in url) def assert_element_visible(self, selector: str, timeout: int = 10000) -> None: """断言元素可见""" element = self.wait_for_selector(selector, timeout=timeout) expect(element).to_be_visible() def assert_element_hidden(self, selector: str, timeout: int = 10000) -> None: """断言元素隐藏""" element = self.page.locator(selector) expect(element).to_be_hidden(timeout=timeout) def hover_element(self, selector: str, timeout: int = 10000) -> None: """悬停在元素上""" self.wait_for_selector(selector, timeout=timeout).hover() def drag_and_drop( self, source_selector: str, target_selector: str, timeout: int = 10000 ) -> None: """拖拽元素""" source = self.wait_for_selector(source_selector, timeout=timeout) target = self.wait_for_selector(target_selector, timeout=timeout) source.drag_to(target) def upload_file(self, selector: str, file_path: str, timeout: int = 10000) -> None: """上传文件""" self.wait_for_selector(selector, timeout=timeout).set_input_files(file_path) def press_key(self, selector: str, key: str, timeout: int = 10000) -> None: """按键""" self.wait_for_selector(selector, timeout=timeout).press(key) def get_element_attribute( self, selector: str, attribute: str, timeout: int = 10000 ) -> str: """获取元素属性""" return ( self.wait_for_selector(selector, timeout=timeout).get_attribute(attribute) or "" ) def get_element_css_property( self, selector: str, property_name: str, timeout: int = 10000 ) -> str: """获取元素CSS属性""" element = self.wait_for_selector(selector, timeout=timeout) return element.evaluate(f"el => getComputedStyle(el).{property_name}") def wait_for_element_to_disappear( self, selector: str, timeout: int = 10000 ) -> None: """等待元素消失""" self.page.wait_for_selector(selector, state="detached", timeout=timeout) def wait_for_response(self, url_pattern: str, timeout: int = 30000): """等待网络响应""" with self.page.expect_response(url_pattern, timeout=timeout) as response_info: pass return response_info.value def wait_for_navigation(self, timeout: int = 30000): """等待页面导航""" self.page.wait_for_load_state("networkidle", timeout=timeout)