feat(cross_end): add cross-platform E2E test support for freight full flow
Add tests/cross_end/ directory with dual-driver architecture:
- conftest.py: Playwright admin_page + Appium app_driver fixtures
- Admin Page Objects: FreightSourcePage, WaybillManagementPage, SettlementPage
- App Page Objects: DriverOrderListPage, DriverTransportPage, DriverHomePage (re-export)
- test_freight_full_flow.py: 8-step end-to-end test (publish → accept → load → unload → accounting → review → shipper pay → platform pay)
- Shared context dict for cross-step data passing
- Graceful skip when either Playwright or Appium is unavailable
- All selectors marked with ⚠️ placeholders for discovery against real environments
Add cross_end marker to pytest.ini for selective execution.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
# cross_end app pages package
|
||||
@@ -0,0 +1,7 @@
|
||||
"""跨端测试 — 司机端首页 Page Object。
|
||||
|
||||
直接复用 tests/app/driver_android/pages/driver_home_page.py。
|
||||
"""
|
||||
|
||||
# re-export existing page object
|
||||
from tests.app.driver_android.pages.driver_home_page import DriverHomePage # noqa: F401
|
||||
@@ -0,0 +1,92 @@
|
||||
"""司机端 APP — 订单列表 + 接单 Page Object。
|
||||
|
||||
Usage:
|
||||
order_list = DriverOrderListPage(driver)
|
||||
order_list.search_and_accept_order(order_no)
|
||||
"""
|
||||
|
||||
from tests.app._base_page import AppBasePage
|
||||
|
||||
|
||||
class DriverOrderListPage(AppBasePage):
|
||||
"""司机端 APP - 订单列表 - 接单。"""
|
||||
|
||||
def __init__(self, driver, platform: str = "android"):
|
||||
super().__init__(driver, platform)
|
||||
# ⚠️ 以下 accessibility_id 需要根据实际 APP 元素 ID 调整
|
||||
# 使用 Appium Desktop Inspector 或 uiautomatorviewer 获取
|
||||
|
||||
# ── 搜索区 ──
|
||||
self.search_bar = "et_search" # ⚠️ 搜索框
|
||||
self.search_input = "et_search_input" # ⚠️ 搜索输入框
|
||||
self.search_btn = "btn_search" # ⚠️ 搜索按钮
|
||||
|
||||
# ── 列表 ──
|
||||
self.order_list_container = "rv_order_list" # ⚠️ 订单列表容器
|
||||
self.order_item_prefix = "order_item_" # ⚠️ 订单项 ID 前缀
|
||||
self.order_detail_btn = "btn_order_detail" # ⚠️ 进入订单详情
|
||||
|
||||
# ── 接单 ──
|
||||
self.accept_order_btn = "btn_accept_order" # ⚠️ 接单按钮
|
||||
self.confirm_accept_btn = "btn_confirm" # ⚠️ 确认接单按钮
|
||||
self.accept_success_text = "接单成功" # ⚠️ 成功提示文本
|
||||
|
||||
def search_order(self, order_no: str) -> None:
|
||||
"""搜索指定订单。"""
|
||||
# 如果搜索框是独立按钮需要先点击展开
|
||||
try:
|
||||
self.tap_by_id(self.search_bar)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.fill_by_id(self.search_input, order_no)
|
||||
self.tap_by_id(self.search_btn)
|
||||
self.driver.implicitly_wait(3)
|
||||
|
||||
def accept_order(self, order_no: str) -> None:
|
||||
"""接受指定订单。
|
||||
|
||||
Args:
|
||||
order_no: 订单号
|
||||
"""
|
||||
# 点击搜索结果中的订单
|
||||
# ⚠️ 实际定位方式取决于列表项如何展示订单号
|
||||
# 策略1: 通过订单号文本点击
|
||||
self.tap_by_text(order_no)
|
||||
self.driver.implicitly_wait(1)
|
||||
|
||||
# 策略2: 通过详情按钮进入
|
||||
# self.tap_by_id(f"{self.order_item_prefix}{order_no}")
|
||||
# self.tap_by_id(self.order_detail_btn)
|
||||
|
||||
def search_and_accept_order(self, order_no: str) -> None:
|
||||
"""搜索订单并接单 — 完整的接单流程。
|
||||
|
||||
Args:
|
||||
order_no: 订单号
|
||||
"""
|
||||
# 1. 搜索
|
||||
self.search_order(order_no)
|
||||
|
||||
# 2. 进入订单详情
|
||||
self.accept_order(order_no)
|
||||
|
||||
# 3. 点击接单
|
||||
self.tap_by_id(self.accept_order_btn)
|
||||
self.driver.implicitly_wait(1)
|
||||
|
||||
# 4. 确认接单 (如有弹窗)
|
||||
try:
|
||||
self.tap_by_id(self.confirm_accept_btn)
|
||||
except Exception:
|
||||
pass # 无确认弹窗则跳过
|
||||
|
||||
self.driver.implicitly_wait(2)
|
||||
|
||||
def verify_order_accepted(self) -> bool:
|
||||
"""验证订单已被接受(成功提示出现)。"""
|
||||
try:
|
||||
self.wait_visible_by_text(self.accept_success_text, timeout_s=5)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
@@ -0,0 +1,132 @@
|
||||
"""司机端 APP — 运输中(装货/卸货) Page Object。
|
||||
|
||||
Usage:
|
||||
transport = DriverTransportPage(driver)
|
||||
transport.load_goods(order_no)
|
||||
transport.unload_goods(order_no)
|
||||
"""
|
||||
|
||||
from tests.app._base_page import AppBasePage
|
||||
|
||||
|
||||
class DriverTransportPage(AppBasePage):
|
||||
"""司机端 APP - 运输中 - 装货/卸货。"""
|
||||
|
||||
def __init__(self, driver, platform: str = "android"):
|
||||
super().__init__(driver, platform)
|
||||
# ⚠️ 以下 accessibility_id 需要根据实际 APP 元素 ID 调整
|
||||
# 使用 Appium Desktop Inspector 或 uiautomatorviewer 获取
|
||||
|
||||
# ── 导航 tab ──
|
||||
self.my_orders_tab = "tab_orders" # ⚠️ 我的运单 tab
|
||||
|
||||
# ── 运单列表/详情 ──
|
||||
self.order_item = "order_item" # ⚠️ 运单项
|
||||
self.order_detail_btn = "btn_order_detail" # ⚠️ 进入详情
|
||||
|
||||
# ── 装货 ──
|
||||
self.load_goods_btn = "btn_load_goods" # ⚠️ 装货按钮
|
||||
self.confirm_load_btn = "btn_confirm" # ⚠️ 确认装货
|
||||
self.upload_load_photo_btn = "btn_upload_photo" # ⚠️ 上传装货照片 (如有)
|
||||
self.load_success_text = "装货成功" # ⚠️ 成功提示
|
||||
|
||||
# ── 卸货 ──
|
||||
self.unload_goods_btn = "btn_unload_goods" # ⚠️ 卸货按钮
|
||||
self.confirm_unload_btn = "btn_confirm" # ⚠️ 确认卸货
|
||||
self.upload_unload_photo_btn = "btn_upload_photo" # ⚠️ 上传卸货照片 (如有)
|
||||
self.unload_success_text = "卸货成功" # ⚠️ 成功提示
|
||||
|
||||
def go_to_my_orders(self) -> None:
|
||||
"""切换到我的运单 tab。"""
|
||||
self.tap_by_id(self.my_orders_tab)
|
||||
self.driver.implicitly_wait(1)
|
||||
|
||||
def find_and_open_order(self, order_no: str) -> None:
|
||||
"""查找并打开指定运单的详情页。
|
||||
|
||||
Args:
|
||||
order_no: 订单号
|
||||
"""
|
||||
# ⚠️ 策略取决于 APP 实际布局:
|
||||
# 策略1: 通过订单号文本定位并点击
|
||||
try:
|
||||
self.tap_by_text(order_no)
|
||||
self.driver.implicitly_wait(1)
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 策略2: 滚动查找
|
||||
for _ in range(5): # 最多滚动 5 次
|
||||
try:
|
||||
self.tap_by_text(order_no)
|
||||
self.driver.implicitly_wait(1)
|
||||
return
|
||||
except Exception:
|
||||
self.scroll_down()
|
||||
|
||||
raise RuntimeError(f"在运单列表中未找到订单: {order_no}")
|
||||
|
||||
def load_goods(self, order_no: str) -> None:
|
||||
"""执行装货流程。
|
||||
|
||||
Args:
|
||||
order_no: 订单号
|
||||
"""
|
||||
self.go_to_my_orders()
|
||||
self.find_and_open_order(order_no)
|
||||
|
||||
# 点击装货
|
||||
self.tap_by_id(self.load_goods_btn)
|
||||
self.driver.implicitly_wait(1)
|
||||
|
||||
# 上传照片 (如有)
|
||||
try:
|
||||
self.tap_by_id(self.upload_load_photo_btn)
|
||||
self.driver.implicitly_wait(1)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 确认装货
|
||||
self.tap_by_id(self.confirm_load_btn)
|
||||
self.driver.implicitly_wait(2)
|
||||
|
||||
def unload_goods(self, order_no: str) -> None:
|
||||
"""执行卸货流程。
|
||||
|
||||
Args:
|
||||
order_no: 订单号
|
||||
"""
|
||||
self.go_to_my_orders()
|
||||
self.find_and_open_order(order_no)
|
||||
|
||||
# 点击卸货
|
||||
self.tap_by_id(self.unload_goods_btn)
|
||||
self.driver.implicitly_wait(1)
|
||||
|
||||
# 上传照片 (如有)
|
||||
try:
|
||||
self.tap_by_id(self.upload_unload_photo_btn)
|
||||
self.driver.implicitly_wait(1)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 确认卸货
|
||||
self.tap_by_id(self.confirm_unload_btn)
|
||||
self.driver.implicitly_wait(2)
|
||||
|
||||
def verify_load_complete(self) -> bool:
|
||||
"""验证装货完成。"""
|
||||
try:
|
||||
self.wait_visible_by_text(self.load_success_text, timeout_s=5)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def verify_unload_complete(self) -> bool:
|
||||
"""验证卸货完成。"""
|
||||
try:
|
||||
self.wait_visible_by_text(self.unload_success_text, timeout_s=5)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
Reference in New Issue
Block a user