Files
Yb-QaAutomationHub/tests/pages/base/web_base_page.py
T
xst 18890c0437 refactor(tests): restructure into layered POM architecture with Allure reporting
- Split framework into core/ (config, logger, db, driver factories, screenshot,
  allure integration) and pages/ (PO layer: base/admin/driver); flatten test
  files per platform (web/admin, app/driver_android, cross_end)
- Slim root conftest.py 656 -> ~100 lines: drop inline CN HTML report, add
  Allure severity mapping (p0/p1/p2 -> blocker/normal/minor) and
  environment.properties; support both tests.*-prefixed and bare imports
- Preserve TC-D2S-001/002 cross-end E2E cases with business logic unchanged,
  add allure epic/feature/story/step annotations
- Deduplicate: single makereport hook, shared failure-screenshot helpers,
  single SCREENSHOTS_DIR constant; deterministic auth-state role loading
- Move DB credentials from db_helper.py into test_config.yaml (database section)
- Move pytest.ini to repo root; switch reporting to allure-pytest
  (drop pytest-html/junitxml); update requirements.txt accordingly
- Delete deprecated scaffolding: test_freight_full_flow + 6 stale page
  objects, iOS/shipper empty shells, dead utils
- Fix flaky vxe-table action-row wait: wait for a visible td instead of
  hidden placeholder rows (fixed shipper-payment timeouts)
- Verified end-to-end against live env: both E2E cases pass (2 passed 3m12s);
  run artifacts (logs/screenshots/allure) included per repo convention
2026-08-17 14:29:52 +08:00

78 lines
2.8 KiB
Python

"""Web Page Object 基类 — 封装所有页面共享的通用操作 (Playwright)。
每个具体页面应继承 BasePage,在 __init__ 中声明所有定位器。
Usage:
class LoginPage(BasePage):
def __init__(self, page):
super().__init__(page)
self.phone_input = page.get_by_placeholder("请输入用户名")
self.login_btn = page.get_by_role("button", name="登录")
"""
from datetime import datetime
from typing import Optional
from playwright.sync_api import Page, Locator, expect
from tests.core.allure_support import attach_png
from tests.core.paths import SCREENSHOTS_DIR
class BasePage:
"""Web 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.get_by_text(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:
"""关键节点截图(存盘 + attach Allure),返回文件路径。"""
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)
attach_png(path, name=name)
return path
# ── 导航 ──
def navigate(self, url: str) -> None:
"""导航到指定 URL 并等待页面加载完成。"""
self.page.goto(url, wait_until="domcontentloaded")