fix: 兼容采购规格面板确定按钮 (#157)
This commit is contained in:
@@ -3,12 +3,17 @@
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import unittest
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from src.pdd_device_service import PddDeviceService
|
||||
from src.pdd_purchase_adapter import PddPurchaseError
|
||||
from src.performance_timing import TaskPerformanceTrace
|
||||
from src.pdd_u2_purchase_adapter import U2PddPurchaseAdapter
|
||||
from src.pdd_u2_purchase_adapter import U2PddLivePurchaseAdapter
|
||||
from src.pdd_u2_purchase_adapter import (
|
||||
U2PddLivePurchaseAdapter,
|
||||
U2PddPurchaseAdapter,
|
||||
_final_submit_targets,
|
||||
_page_kind,
|
||||
)
|
||||
|
||||
|
||||
GOODS_URL = "https://mobile.yangkeduo.com/goods.html?goods_id=753136429979"
|
||||
@@ -60,6 +65,36 @@ def panel_xml(quantity: int = 1) -> str:
|
||||
</hierarchy>"""
|
||||
|
||||
|
||||
def contextual_confirm_panel_xml(quantity: int = 1) -> str:
|
||||
"""模拟商品 897186799891 的非滚动“确定”规格面板。"""
|
||||
|
||||
return f"""<hierarchy>
|
||||
<node package="com.xunmeng.pinduoduo" bounds="[0,0][1080,2340]">
|
||||
<node package="com.xunmeng.pinduoduo" class="android.widget.RelativeLayout"
|
||||
bounds="[0,720][1080,2214]" visible-to-user="true" enabled="true">
|
||||
<node package="com.xunmeng.pinduoduo" text="确认款式"
|
||||
bounds="[438,746][642,814]"/>
|
||||
<node text="券后 ¥8.17" bounds="[324,852][573,919]"/>
|
||||
<node text="已选择: 黑色 均码" bounds="[324,1009][1044,1062]"/>
|
||||
<node class="android.widget.EditText" text="{quantity}"
|
||||
bounds="[408,1080][489,1155]"/>
|
||||
<node content-desc="减少数量" clickable="true" enabled="true"
|
||||
visible-to-user="true" bounds="[324,1080][402,1155]"/>
|
||||
<node content-desc="增加数量" clickable="true" enabled="true"
|
||||
visible-to-user="true" bounds="[495,1080][573,1155]"/>
|
||||
<node text="颜色分类" bounds="[36,1214][216,1267]"/>
|
||||
<node text="黑色" clickable="true" selected="true"
|
||||
bounds="[36,1295][276,1382]"/>
|
||||
<node text="尺码" bounds="[36,1684][126,1737]"/>
|
||||
<node text="均码" clickable="true" selected="true"
|
||||
bounds="[36,1765][711,1852]"/>
|
||||
<node text="确定" clickable="true" enabled="true"
|
||||
visible-to-user="true" bounds="[0,2067][1080,2214]"/>
|
||||
</node>
|
||||
</node>
|
||||
</hierarchy>"""
|
||||
|
||||
|
||||
class FakeEditor:
|
||||
def __init__(self, device) -> None:
|
||||
self.device = device
|
||||
@@ -186,6 +221,24 @@ class PanelDoesNotOpenDevice(FakeDevice):
|
||||
self.clicks.append((x, y))
|
||||
|
||||
|
||||
class ContextualConfirmPanelDevice(FakeDevice):
|
||||
def dump_hierarchy(self):
|
||||
if not self.has_opened:
|
||||
return '<hierarchy><node package="com.xunmeng.pinduoduo" /></hierarchy>'
|
||||
if self.mode == "special":
|
||||
return self.special_xml
|
||||
if self.mode == "panel":
|
||||
return contextual_confirm_panel_xml(self.quantity)
|
||||
return home_xml()
|
||||
|
||||
def click(self, x, y):
|
||||
self.clicks.append((x, y))
|
||||
if self.mode == "panel" and 495 <= x <= 573 and 1080 <= y <= 1155:
|
||||
self.quantity += 1
|
||||
return
|
||||
self.mode = "panel"
|
||||
|
||||
|
||||
class U2PddPurchaseAdapterTest(unittest.TestCase):
|
||||
def _adapter(self, device, calls):
|
||||
def select_color_fn(_device, _xml, target, **_kwargs):
|
||||
@@ -252,6 +305,75 @@ class U2PddPurchaseAdapterTest(unittest.TestCase):
|
||||
self.assertEqual(device.editor_values, [])
|
||||
self.assertEqual(device.app_wait_calls, 0)
|
||||
|
||||
def test_contextual_confirm_panel_is_reliable_without_clicking_confirm(self):
|
||||
device = ContextualConfirmPanelDevice()
|
||||
calls = []
|
||||
|
||||
def select_color_fn(_device, _xml, target, **_kwargs):
|
||||
calls.append(("color", target))
|
||||
return target == "黑色"
|
||||
|
||||
def select_size_fn(_device, _xml, target, **_kwargs):
|
||||
calls.append(("size", target))
|
||||
return target == "均码"
|
||||
|
||||
adapter = U2PddPurchaseAdapter(
|
||||
"USB-001",
|
||||
device_service=PddDeviceService(connector=lambda _serial: device),
|
||||
sleeper=lambda _seconds: None,
|
||||
select_color_fn=select_color_fn,
|
||||
select_size_fn=select_size_fn,
|
||||
)
|
||||
|
||||
adapter.open_goods(GOODS_URL)
|
||||
adapter.select_options({"color": "黑色", "size": "均码"})
|
||||
state = adapter.read_state()
|
||||
adapter.stop_before_submit()
|
||||
|
||||
self.assertEqual(state.page_kind, "order_confirmation")
|
||||
self.assertEqual(state.submit_candidate_count, 1)
|
||||
self.assertEqual(calls, [("color", "黑色"), ("size", "均码")])
|
||||
# dry-run 只点击商品页采购入口,不点击底部“确定”。
|
||||
self.assertEqual(device.clicks, [(790, 2214)])
|
||||
adapter.close()
|
||||
|
||||
def test_plain_confirm_dialog_is_not_a_submit_target(self):
|
||||
root = ET.fromstring("""<hierarchy>
|
||||
<node package="com.xunmeng.pinduoduo" bounds="[0,0][1080,2340]">
|
||||
<node package="com.xunmeng.pinduoduo" text="提示"
|
||||
bounds="[100,800][980,1400]">
|
||||
<node package="com.xunmeng.pinduoduo" text="确定"
|
||||
clickable="true" enabled="true" visible-to-user="true"
|
||||
bounds="[300,1250][780,1380]"/>
|
||||
</node>
|
||||
</node>
|
||||
</hierarchy>""")
|
||||
|
||||
self.assertEqual(_page_kind(root, ""), "unknown")
|
||||
self.assertEqual(_final_submit_targets(root), [])
|
||||
|
||||
def test_live_adapter_clicks_contextual_confirm_only_once(self):
|
||||
device = ContextualConfirmPanelDevice()
|
||||
adapter = U2PddLivePurchaseAdapter(
|
||||
"USB-001",
|
||||
device_service=PddDeviceService(connector=lambda _serial: device),
|
||||
sleeper=lambda _seconds: None,
|
||||
select_color_fn=lambda *_args, **_kwargs: True,
|
||||
select_size_fn=lambda *_args, **_kwargs: True,
|
||||
)
|
||||
adapter.open_goods(GOODS_URL)
|
||||
adapter.select_options({"color": "黑色", "size": "均码"})
|
||||
|
||||
adapter.submit_order_once()
|
||||
with self.assertRaises(PddPurchaseError) as raised:
|
||||
adapter.submit_order_once()
|
||||
|
||||
self.assertEqual(raised.exception.code, "PURCHASE_SUBMIT_ALREADY_ATTEMPTED")
|
||||
# 采购入口一次,底部“确定”一次。
|
||||
self.assertEqual(len(device.clicks), 2)
|
||||
self.assertEqual(device.clicks[-1], (540, 2140))
|
||||
adapter.close()
|
||||
|
||||
def test_reliable_pdd_tree_skips_repeated_app_current(self):
|
||||
device = FakeDevice()
|
||||
adapter = self._adapter(device, [])
|
||||
|
||||
Reference in New Issue
Block a user