refactor(locators): 文本锚点优先的多候选定位 + 失败 DOM 快照
定位健壮性原则: 语义文本 > 组件库稳定 class > 层级结构 > nth-child/绝对路径。
Web (freight_accounting_page):
- 新增 BasePage.first_matching(*候选): 文本锚点优先命中, 旧结构路径兜底
- 13 处定位器改造: 核算/审核通过/确认打款按钮(get_by_role 文本)、
车队长/收款模式表单(.el-form-item label 锚定)、光大单选(label:has-text)、
获取验证码/验证码输入(按钮同行锚定)、message-box 确定等
- 手机号单选保留旧结构路径优先(弹窗含多组电话单选, 文本有歧义)
- 已实况验证: 获取验证码/确认打款/光大锚点唯一命中
App (freight_flow_page):
- 货源 tab: text("货源") 文本锚点优先(登录流程持续验证), 位置序号兜底
- 货源单列表项: RecyclerView(lRrcyclerView) 第一个子项, 全局 instance(3) 兜底
- 选图按钮(ImageView instance(2))保留: 需在途订单才能探测, 当前可用
失败诊断:
- Web 失败时 attach 页面 DOM 快照, App 失败时 attach page_source 到 Allure
验收: 实况锚点解析全部唯一命中; 两条 E2E 通过且 DB 终态均为全流程完成
(status=5, 已结算, 双支付完成)。
This commit is contained in:
@@ -25,16 +25,22 @@ class DriverFreightFlowPage(AppBasePage):
|
||||
super().__init__(driver, platform)
|
||||
|
||||
# ── 底部导航 ──
|
||||
# 0=首页, 1=货源, 2=消息, 3=我的
|
||||
self.freight_source_tab = (
|
||||
AppiumBy.ANDROID_UIAUTOMATOR,
|
||||
'new UiSelector().resourceId("com.arpa.ynchenggangdriver:id/fixed_bottom_navigation_icon").instance(1)',
|
||||
# 文本锚点优先(登录流程每轮运行都在验证 text("货源") 可用),
|
||||
# 位置序号兜底(0=首页, 1=货源, 2=消息, 3=我的)
|
||||
self.freight_source_tab_candidates = (
|
||||
(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("货源")'),
|
||||
(AppiumBy.ANDROID_UIAUTOMATOR,
|
||||
'new UiSelector().resourceId("com.arpa.ynchenggangdriver:id/fixed_bottom_navigation_icon").instance(1)'),
|
||||
)
|
||||
|
||||
# ── 货源单列表 ──
|
||||
self.freight_order_item = (
|
||||
AppiumBy.ANDROID_UIAUTOMATOR,
|
||||
'new UiSelector().className("android.widget.RelativeLayout").instance(3)',
|
||||
# 锚定 RecyclerView 第一个子项(语义: 列表第一行),全局序号兜底
|
||||
self.freight_order_item_candidates = (
|
||||
(AppiumBy.ANDROID_UIAUTOMATOR,
|
||||
'new UiSelector().resourceId("com.arpa.ynchenggangdriver:id/lRrcyclerView")'
|
||||
'.childSelector(new UiSelector().className("android.widget.RelativeLayout").instance(0))'),
|
||||
(AppiumBy.ANDROID_UIAUTOMATOR,
|
||||
'new UiSelector().className("android.widget.RelativeLayout").instance(3)'),
|
||||
)
|
||||
|
||||
# ── 数量 ──
|
||||
@@ -93,6 +99,17 @@ class DriverFreightFlowPage(AppBasePage):
|
||||
except Exception:
|
||||
logger.warning(f"可选元素 '{text}' 在 {timeout_s}s 内未出现,跳过点击")
|
||||
|
||||
def _tap_first_matching(self, *locators, timeout_s: float = 5.0):
|
||||
"""依次尝试多个候选定位器点击,第一个成功的生效;全部失败抛最后一个异常。"""
|
||||
last_exc: Exception | None = None
|
||||
for loc in locators:
|
||||
try:
|
||||
self._wait_and_click(loc, timeout_s)
|
||||
return
|
||||
except Exception as e:
|
||||
last_exc = e
|
||||
raise last_exc if last_exc else RuntimeError("无候选定位器")
|
||||
|
||||
def _swipe_up(self, locator=None, duration_ms: int = 800):
|
||||
"""向上滑动。指定 locator 时在元素内滑动,否则全屏滑动。"""
|
||||
if locator:
|
||||
@@ -124,11 +141,11 @@ class DriverFreightFlowPage(AppBasePage):
|
||||
|
||||
def open_freight_tab(self):
|
||||
"""切换到底部导航的货源 tab。"""
|
||||
self._wait_and_click(self.freight_source_tab)
|
||||
self._tap_first_matching(*self.freight_source_tab_candidates)
|
||||
|
||||
def select_freight_item(self):
|
||||
"""选择货源单列表中的一项。"""
|
||||
self._wait_and_click(self.freight_order_item)
|
||||
self._tap_first_matching(*self.freight_order_item_candidates)
|
||||
|
||||
def fill_quantity(self, quantity: str):
|
||||
"""填写接单数量。"""
|
||||
|
||||
Reference in New Issue
Block a user