Files
张翔 08ea5fbe98 feat(admin): 添加用户管理相关文件
添加用户管理视图、API和状态管理文件
2026-03-28 14:37:29 +08:00

215 lines
7.5 KiB
Python

"""
基础页面类
所有页面对象的基类,提供通用方法。
"""
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)