refactor(pages): PO 层框架化 — 细粒度操作 + 显式等待 + 消除静默吞异常

DriverFreightFlowPage (APP):
- 流程方法拆为单一职责的细粒度操作(open_freight_tab/fill_quantity/upload_photo_from_album/...),
  流程方法仅做编排,公共 API 不变,用例零改动
- 移除全部 implicitly_wait 全局副作用,每步显式等待目标元素
- 容错点击(已知晓/确定)改为 _tap_by_text_if_present,跳过时记录 warning

FreightAccountingPage (Web):
- _go/_click_search 改显式等待(搜索框出现/列表数据行或空态),替代固定 2s/1s sleep
- 表格等待升级为 td:visible,避开 vxe-table 固定列隐藏占位单元格
- 点击行操作按钮后显式等待弹窗出现,替代固定 800ms
- 车队长下拉选择改显式等待下拉项渲染
- 可选弹窗操作统一收敛到 _try_click,跳过时记录 warning;关键确认按钮保持严格

两条 E2E 用例在真实环境回归通过(TC-D2S-001 116s / TC-D2S-002 112s)。
This commit is contained in:
xst
2026-09-02 11:59:46 +08:00
parent 6ee7ade67d
commit 2aca405267
310 changed files with 78629 additions and 157 deletions
+98 -62
View File
@@ -1,15 +1,22 @@
"""司机 APP 货运流程 Page Object — 货源→接单→装货→卸货→获取单号。
"""司机 APP 货运流程 Page Object — 细粒度操作 + 流程编排。
基于 resources/selectors/正常流程-司机.md 中的元素定位。
使用 Appium UiAutomator2 定位策略。
结构:
__init__ 定位器集中声明(与 resources/selectors/正常流程-司机.md 对应)
基础操作 显式等待封装(WebDriverWait),无常驻固定 sleep
细粒度操作 单一职责的页面操作(open_freight_tab / fill_quantity / ...)
流程编排 accept_order_flow / load_goods_flow / ... 供用例调用,仅做步骤组合
"""
import logging
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from tests.pages.base.app_base_page import AppBasePage
logger = logging.getLogger(__name__)
class DriverFreightFlowPage(AppBasePage):
"""司机端 APP - 货运完整流程(货源→接单→装货→卸货→获取单号)。"""
@@ -60,7 +67,7 @@ class DriverFreightFlowPage(AppBasePage):
self.confirm_btn = (AppiumBy.ID, "com.arpa.ynchenggangdriver:id/ok_tv3")
# ════════════════════════════════════════════════════════════
# 基础操作封装
# 基础操作(显式等待)
# ════════════════════════════════════════════════════════════
def _wait_and_click(self, locator, timeout_s: float = 10.0):
@@ -79,12 +86,12 @@ class DriverFreightFlowPage(AppBasePage):
locator = (AppiumBy.ANDROID_UIAUTOMATOR, f'new UiSelector().text("{text}")')
self._wait_and_click(locator, timeout_s)
def _safe_tap_by_text(self, text: str, timeout_s: float = 5.0):
"""容错点击:元素不存在时静默跳过。"""
def _tap_by_text_if_present(self, text: str, timeout_s: float = 5.0):
"""可选元素点击:不出现时记录日志并跳过(不静默吞掉)。"""
try:
self._tap_by_text(text, timeout_s)
except Exception:
pass
logger.warning(f"可选元素 '{text}' 在 {timeout_s}s 内未出现,跳过点击")
def _swipe_up(self, locator=None, duration_ms: int = 800):
"""向上滑动。指定 locator 时在元素内滑动,否则全屏滑动。"""
@@ -112,68 +119,73 @@ class DriverFreightFlowPage(AppBasePage):
)
# ════════════════════════════════════════════════════════════
# 接单流程 (步骤 1-5)
# 细粒度操作: 接单
# ════════════════════════════════════════════════════════════
def accept_order_flow(self, quantity: str = "1"):
"""接单完整流程:货源 tab → 货源单 → 数量 → 协议 → 接单。"""
self._wait_and_click(self.freight_source_tab) # ① 点击货源
self.driver.implicitly_wait(1)
self._wait_and_click(self.freight_order_item) # ② 点击货源单
self.driver.implicitly_wait(2)
self._wait_and_fill(self.quantity_input, quantity) # ③ 填写数量
self._wait_and_click(self.agreement_checkbox) # ④ 勾选协议
self._wait_and_click(self.accept_order_btn) # ⑤ 接单
self.driver.implicitly_wait(2)
def open_freight_tab(self):
"""切换到底部导航的货源 tab。"""
self._wait_and_click(self.freight_source_tab)
def select_freight_item(self):
"""选择货源单列表中的一项。"""
self._wait_and_click(self.freight_order_item)
def fill_quantity(self, quantity: str):
"""填写接单数量。"""
self._wait_and_fill(self.quantity_input, quantity)
def check_agreement(self):
"""勾选协议。"""
self._wait_and_click(self.agreement_checkbox)
def tap_accept_order(self):
"""点击接单按钮。"""
self._wait_and_click(self.accept_order_btn)
# ════════════════════════════════════════════════════════════
# 装货流程 (步骤 6-17)
# 细粒度操作: 装货/卸货
# ════════════════════════════════════════════════════════════
def load_goods_flow(self, weight: str = "10"):
"""装货完整流程。"""
self._tap_by_text("装货") # ⑥ 点击装货
self.driver.implicitly_wait(1)
self._wait_and_fill(self.weight_input, weight) # ⑧ 输入吨位
self._wait_and_click(self.select_image_btn) # ⑨ 选择图片
self.driver.implicitly_wait(1)
self._wait_and_click(self.album_btn) # ⑩ 相册
self.driver.implicitly_wait(1)
self._wait_and_click(self.image_checkbox) # ⑪ 勾选图片
self._wait_and_click(self.done_btn) # ⑫ 完成
self.driver.implicitly_wait(2)
self._wait_and_click(self.agreement_checkbox) # ⑬ 勾选协议
self._wait_and_click(self.load_unload_confirm) # ⑭ 装货确认
self.driver.implicitly_wait(1)
self._swipe_up(self.contract_webview) # ⑮ 滑动合同
self.driver.implicitly_wait(1)
self._safe_tap_by_text("已知晓", timeout_s=5.0) # ⑯ 已知晓(容错)
self._safe_tap_by_text("确定", timeout_s=5.0) # ⑰ 确定(容错)
self.driver.implicitly_wait(2)
def open_load_dialog(self):
"""点击"装货"入口。"""
self._tap_by_text("装货")
def open_unload_dialog(self):
"""点击"卸货"入口。"""
self._tap_by_text("卸货")
def fill_weight(self, weight: str):
"""输入吨位。"""
self._wait_and_fill(self.weight_input, weight)
def upload_photo_from_album(self):
"""上传图片: 选择图片 → 相册 → 勾选图片 → 完成。"""
self._wait_and_click(self.select_image_btn)
self._wait_and_click(self.album_btn)
self._wait_and_click(self.image_checkbox)
self._wait_and_click(self.done_btn)
def confirm_loading(self):
"""装货确认: 勾选协议 → 点击装货确认。"""
self._wait_and_click(self.agreement_checkbox)
self._wait_and_click(self.load_unload_confirm)
def confirm_unloading(self):
"""卸货确认: 点击卸货确认。"""
self._wait_and_click(self.load_unload_confirm)
def sign_contract(self):
"""签署合同: 滑动合同 webview → 已知晓(可选) → 确定(可选)。"""
self._swipe_up(self.contract_webview)
self._tap_by_text_if_present("已知晓")
self._tap_by_text_if_present("确定")
def tap_confirm_if_present(self):
"""点击"确定"弹窗按钮(可选,部分流程不弹)。"""
self._tap_by_text_if_present("确定")
# ════════════════════════════════════════════════════════════
# 卸货流程 (步骤 19-27)
# ════════════════════════════════════════════════════════════
def unload_goods_flow(self, weight: str = "10"):
"""卸货完整流程。"""
self._tap_by_text("卸货") # ⑲ 点击卸货
self.driver.implicitly_wait(1)
self._wait_and_fill(self.weight_input, weight) # ㉑ 输入吨位
self._wait_and_click(self.select_image_btn) # ㉒ 选择图片
self.driver.implicitly_wait(1)
self._wait_and_click(self.album_btn) # ㉓ 相册
self.driver.implicitly_wait(1)
self._wait_and_click(self.image_checkbox) # ㉔ 勾选图片
self._wait_and_click(self.done_btn) # ㉕ 完成
self.driver.implicitly_wait(2)
self._wait_and_click(self.load_unload_confirm) # ㉖ 卸货确认
self.driver.implicitly_wait(1)
self._safe_tap_by_text("确定", timeout_s=5.0) # ㉗ 确定(容错)
self.driver.implicitly_wait(2)
# ════════════════════════════════════════════════════════════
# 获取单号 (步骤 28)
# 查询
# ════════════════════════════════════════════════════════════
def get_transport_order_no(self, timeout_s: float = 10.0) -> str:
@@ -193,9 +205,33 @@ class DriverFreightFlowPage(AppBasePage):
return full_text.replace("运输单号:", "").replace("运输单号:", "").strip()
# ════════════════════════════════════════════════════════════
# 全流程
# 流程编排(供用例调用)
# ════════════════════════════════════════════════════════════
def accept_order_flow(self, quantity: str = "1"):
"""接单完整流程:货源 tab → 货源单 → 数量 → 协议 → 接单。"""
self.open_freight_tab()
self.select_freight_item()
self.fill_quantity(quantity)
self.check_agreement()
self.tap_accept_order()
def load_goods_flow(self, weight: str = "10"):
"""装货完整流程:装货 → 吨位 → 拍照上传 → 确认 → 合同。"""
self.open_load_dialog()
self.fill_weight(weight)
self.upload_photo_from_album()
self.confirm_loading()
self.sign_contract()
def unload_goods_flow(self, weight: str = "10"):
"""卸货完整流程:卸货 → 吨位 → 拍照上传 → 确认 → 确定。"""
self.open_unload_dialog()
self.fill_weight(weight)
self.upload_photo_from_album()
self.confirm_unloading()
self.tap_confirm_if_present()
def full_driver_flow(self, quantity: str = "1", load_weight: str = "10",
unload_weight: str = "10") -> str:
"""执行完整司机端流程:接单→装货→卸货→返回运输单号。"""