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
View File
@@ -0,0 +1,98 @@
<?xml version="1.0" encoding="utf-8"?><testsuites name="pytest tests"><testsuite name="pytest" errors="1" failures="2" skipped="0" tests="3" time="297.930" timestamp="2026-07-29T14:57:35.238149+08:00" hostname="Y8126"><testcase classname="cross_end.tests.test_driver_to_settlement.TestDriverToSettlement" name="test_driver_to_settlement_full_flow" time="111.649"><failure message="playwright._impl._errors.TimeoutError: Page.wait_for_selector: Timeout 5000ms exceeded.&#10;Call log:&#10; - waiting for locator(&quot;table tbody tr&quot;) to be visible&#10; 14 × locator resolved to 5 elements. Proceeding with the first one: &lt;tr&gt;…&lt;/tr&gt;">tests\cross_end\tests\test_driver_to_settlement.py:133: in test_driver_to_settlement_full_flow
admin_accounting.do_shipper_payment(transport_order_no)
tests\cross_end\pages\admin\freight_accounting_page.py:315: in do_shipper_payment
self._click_table_action(button_index=2)
tests\cross_end\pages\admin\freight_accounting_page.py:82: in _click_table_action
self.page.wait_for_selector("table tbody tr", timeout=5_000)
D:\Python\Python312\Lib\site-packages\playwright\sync_api\_generated.py:8928: in wait_for_selector
self._sync(
D:\Python\Python312\Lib\site-packages\playwright\_impl\_page.py:427: in wait_for_selector
return await self._main_frame.wait_for_selector(**locals_to_params(locals()))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
D:\Python\Python312\Lib\site-packages\playwright\_impl\_frame.py:394: in wait_for_selector
await self._channel.send(
D:\Python\Python312\Lib\site-packages\playwright\_impl\_connection.py:69: in send
return await self._connection.wrap_api_call(
D:\Python\Python312\Lib\site-packages\playwright\_impl\_connection.py:563: in wrap_api_call
raise rewrite_error(error, f"{parsed_st['apiName']}: {error}") from None
E playwright._impl._errors.TimeoutError: Page.wait_for_selector: Timeout 5000ms exceeded.
E Call log:
E - waiting for locator("table tbody tr") to be visible
E 14 × locator resolved to 5 elements. Proceeding with the first one: &lt;tr&gt;…&lt;/tr&gt;</failure></testcase><testcase classname="cross_end.tests.test_driver_to_settlement.TestDriverToSettlementTeamLeader" name="test_driver_to_settlement_team_leader_flow" time="185.546"><failure message="playwright._impl._errors.TimeoutError: Locator.click: Timeout 120000ms exceeded.&#10;Call log:&#10; - waiting for locator(&quot;#app &gt; div &gt; div.main-container.hasTagsView &gt; section &gt; div &gt; div &gt; div &gt; div.vxe-modal--wrapper.type--modal.is--animat.lock--view.is--mask.is--visible.is--active &gt; div &gt; div.vxe-modal--body &gt; div.vxe-modal--content &gt; div &gt; form &gt; div:nth-child(2) &gt; div:nth-child(5) &gt; div &gt; div &gt; div &gt; div &gt; input&quot;).first&#10; - locator resolved to &lt;input type=&quot;text&quot; placeholder=&quot;请选择&quot; autocomplete=&quot;off&quot; readonly=&quot;readonly&quot; disabled=&quot;disabled&quot; class=&quot;el-input__inner&quot;/&gt;&#10; - attempting click action&#10; 2 × waiting for element to be visible, enabled and stable&#10; - element is not enabled&#10; - retrying click action&#10; - waiting 20ms&#10; 2 × waiting for element to be visible, enabled and stable&#10; - element is not enabled&#10; - retrying click action&#10; - waiting 100ms&#10; 231 × waiting for element to be visible, enabled and stable&#10; - element is not enabled&#10; - retrying click action&#10; - waiting 500ms">tests\cross_end\tests\test_driver_to_settlement.py:225: in test_driver_to_settlement_team_leader_flow
admin_accounting.do_freight_accounting(
tests\cross_end\pages\admin\freight_accounting_page.py:158: in do_freight_accounting
self._select_team_leader_payment()
tests\cross_end\pages\admin\freight_accounting_page.py:207: in _select_team_leader_payment
payment_mode_input.click()
D:\Python\Python312\Lib\site-packages\playwright\sync_api\_generated.py:17672: in click
self._sync(
D:\Python\Python312\Lib\site-packages\playwright\_impl\_locator.py:163: in click
return await self._frame._click(self._selector, strict=True, **params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
D:\Python\Python312\Lib\site-packages\playwright\_impl\_frame.py:591: in _click
await self._channel.send("click", self._timeout, locals_to_params(locals()))
D:\Python\Python312\Lib\site-packages\playwright\_impl\_connection.py:69: in send
return await self._connection.wrap_api_call(
D:\Python\Python312\Lib\site-packages\playwright\_impl\_connection.py:563: in wrap_api_call
raise rewrite_error(error, f"{parsed_st['apiName']}: {error}") from None
E playwright._impl._errors.TimeoutError: Locator.click: Timeout 120000ms exceeded.
E Call log:
E - waiting for locator("#app &gt; div &gt; div.main-container.hasTagsView &gt; section &gt; div &gt; div &gt; div &gt; div.vxe-modal--wrapper.type--modal.is--animat.lock--view.is--mask.is--visible.is--active &gt; div &gt; div.vxe-modal--body &gt; div.vxe-modal--content &gt; div &gt; form &gt; div:nth-child(2) &gt; div:nth-child(5) &gt; div &gt; div &gt; div &gt; div &gt; input").first
E - locator resolved to &lt;input type="text" placeholder="请选择" autocomplete="off" readonly="readonly" disabled="disabled" class="el-input__inner"/&gt;
E - attempting click action
E 2 × waiting for element to be visible, enabled and stable
E - element is not enabled
E - retrying click action
E - waiting 20ms
E 2 × waiting for element to be visible, enabled and stable
E - element is not enabled
E - retrying click action
E - waiting 100ms
E 231 × waiting for element to be visible, enabled and stable
E - element is not enabled
E - retrying click action
E - waiting 500ms</failure></testcase><testcase classname="cross_end.tests.test_driver_to_settlement.TestDriverToSettlementTeamLeader" name="test_driver_to_settlement_team_leader_flow" time="0.158"><error message="failed on teardown with &quot;selenium.common.exceptions.InvalidSessionIdException: Message: A session is either terminated or not started; For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#invalidsessionidexception&#10;Stacktrace:&#10;NoSuchDriverError: A session is either terminated or not started&#10; at asyncHandler (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\@appium\base-driver\build\lib\protocol\protocol.js:336:23)&#10; at C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\@appium\base-driver\build\lib\protocol\protocol.js:504:14&#10; at Layer.handleRequest (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\lib\layer.js:152:17)&#10; at next (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\lib\route.js:157:13)&#10; at Route.dispatch (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\lib\route.js:117:3)&#10; at handle (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:435:11)&#10; at Layer.handleRequest (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\lib\layer.js:152:17)&#10; at C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:295:15&#10; at param (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:600:14)&#10; at param (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:610:14)&#10; at processParams (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:664:3)&#10; at next (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:291:5)&#10; at logger (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\morgan\index.js:170:5)&#10; at Layer.handleRequest (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\lib\layer.js:152:17)&#10; at trimPrefix (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:342:13)&#10; at C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:297:9&#10; at processParams (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:582:12)&#10; at next (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:291:5)&#10; at read (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\body-parser\lib\read.js:53:5)&#10; at jsonParser (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\body-parser\lib\types\json.js:88:5)&#10; at Layer.handleRequest (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\lib\layer.js:152:17)&#10; at trimPrefix (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:342:13)&quot;">tests\cross_end\conftest.py:100: in app_driver
save_failure_screenshot(d, cfg, "android", request)
tests\app\_shared.py:229: in save_failure_screenshot
driver.save_screenshot(path)
D:\Python\Python312\Lib\site-packages\selenium\webdriver\remote\webdriver.py:979: in save_screenshot
return self.get_screenshot_as_file(filename)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
D:\Python\Python312\Lib\site-packages\selenium\webdriver\remote\webdriver.py:956: in get_screenshot_as_file
png = self.get_screenshot_as_png()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
D:\Python\Python312\Lib\site-packages\selenium\webdriver\remote\webdriver.py:987: in get_screenshot_as_png
return b64decode(self.get_screenshot_as_base64().encode("ascii"))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
D:\Python\Python312\Lib\site-packages\selenium\webdriver\remote\webdriver.py:997: in get_screenshot_as_base64
return self.execute(Command.SCREENSHOT)["value"]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
D:\Python\Python312\Lib\site-packages\selenium\webdriver\remote\webdriver.py:492: in execute
self.error_handler.check_response(response)
D:\Python\Python312\Lib\site-packages\appium\webdriver\errorhandler.py:125: in check_response
raise exception_class(msg=message, stacktrace=format_stacktrace(stacktrace))
E selenium.common.exceptions.InvalidSessionIdException: Message: A session is either terminated or not started; For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#invalidsessionidexception
E Stacktrace:
E NoSuchDriverError: A session is either terminated or not started
E at asyncHandler (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\@appium\base-driver\build\lib\protocol\protocol.js:336:23)
E at C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\@appium\base-driver\build\lib\protocol\protocol.js:504:14
E at Layer.handleRequest (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\lib\layer.js:152:17)
E at next (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\lib\route.js:157:13)
E at Route.dispatch (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\lib\route.js:117:3)
E at handle (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:435:11)
E at Layer.handleRequest (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\lib\layer.js:152:17)
E at C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:295:15
E at param (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:600:14)
E at param (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:610:14)
E at processParams (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:664:3)
E at next (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:291:5)
E at logger (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\morgan\index.js:170:5)
E at Layer.handleRequest (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\lib\layer.js:152:17)
E at trimPrefix (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:342:13)
E at C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:297:9
E at processParams (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:582:12)
E at next (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:291:5)
E at read (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\body-parser\lib\read.js:53:5)
E at jsonParser (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\body-parser\lib\types\json.js:88:5)
E at Layer.handleRequest (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\lib\layer.js:152:17)
E at trimPrefix (C:\Users\23184\AppData\Roaming\npm\node_modules\appium\node_modules\router\index.js:342:13)</error></testcase></testsuite></testsuites>