aade9e39d0
- tests/config: 环境配置 (.yaml) + 配置加载器 - tests/utils: 日志工具 + 测试数据生成器 - tests/web: Playwright conftest + Page Object 基类 + 登录页 + 导航栏组件 + 登录用例(6条) - tests/app: Appium conftest + Page Object 基类 + 司机端首页 + 登录用例(3条) - pytest.ini: markers(p0/p1/p2/smoke/web/app) + 运行配置 - 所有用例按最佳实践采用 Page Object 模式 + data-testid 优先定位 + 失败自动截图
77 lines
2.6 KiB
Python
77 lines
2.6 KiB
Python
"""Page Object 基类 — 封装所有页面共享的通用操作。
|
|
|
|
每个具体页面应继承 BasePage,在 __init__ 中声明所有定位器。
|
|
|
|
Usage:
|
|
class LoginPage(BasePage):
|
|
def __init__(self, page):
|
|
super().__init__(page)
|
|
self.phone_input = page.getByPlaceholder("请输入用户名")
|
|
self.login_btn = page.getByRole("button", name="登录")
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
from playwright.sync_api import Page, Locator, expect
|
|
|
|
SCREENSHOTS_DIR = Path("output/screenshots")
|
|
|
|
|
|
class BasePage:
|
|
"""Page Object 基类。"""
|
|
|
|
def __init__(self, page: Page, timeout_ms: int = 30_000):
|
|
self.page = page
|
|
self.timeout = timeout_ms
|
|
|
|
# ── 通用等待 ──
|
|
|
|
def wait_visible(self, locator: Locator, timeout_ms: Optional[int] = None) -> None:
|
|
"""等待元素可见。"""
|
|
expect(locator).to_be_visible(timeout=timeout_ms or self.timeout)
|
|
|
|
def wait_hidden(self, locator: Locator, timeout_ms: Optional[int] = None) -> None:
|
|
"""等待元素消失(如 Loading 遮罩)。"""
|
|
expect(locator).to_be_hidden(timeout=timeout_ms or self.timeout)
|
|
|
|
def wait_for_text(self, text: str, timeout_ms: Optional[int] = None) -> None:
|
|
"""等待页面上出现指定文本。"""
|
|
expect(self.page.getByText(text)).to_be_visible(timeout=timeout_ms or self.timeout)
|
|
|
|
# ── 通用操作 ──
|
|
|
|
def fill_field(self, locator: Locator, value: str) -> None:
|
|
"""安全填写输入框(先等待可见,再清空后填写)。"""
|
|
self.wait_visible(locator)
|
|
locator.clear()
|
|
locator.fill(value)
|
|
|
|
def click_when_ready(self, locator: Locator) -> None:
|
|
"""安全点击(等待可见 + 可操作)。"""
|
|
self.wait_visible(locator)
|
|
locator.click()
|
|
|
|
def get_text(self, locator: Locator) -> str:
|
|
"""获取元素的 inner text。"""
|
|
self.wait_visible(locator)
|
|
return locator.inner_text()
|
|
|
|
# ── 截图 ──
|
|
|
|
def screenshot(self, name: str, full_page: bool = False) -> 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}_{ts}.png")
|
|
self.page.screenshot(path=path, full_page=full_page)
|
|
return path
|
|
|
|
# ── 导航 ──
|
|
|
|
def navigate(self, url: str) -> None:
|
|
"""导航到指定 URL 并等待页面加载完成。"""
|
|
self.page.goto(url, wait_until="domcontentloaded")
|