fix: 采购选色前归位规格面板 (#230)

This commit is contained in:
chengma
2026-08-14 18:10:56 +08:00
parent cbe8d0347b
commit fee40284b3
2 changed files with 401 additions and 0 deletions
+141
View File
@@ -98,6 +98,53 @@ def contextual_confirm_panel_xml(quantity: int = 1) -> str:
</hierarchy>"""
def vertically_scrolled_purchase_panel_xml(*, at_color_top: bool) -> str:
"""构造规格面板位于颜色顶部或停在中部的脱敏控件树。"""
if at_color_top:
scroll_content = """
<node text="颜色分类" bounds="[32,1120][190,1170]"/>
<node text="黑色" clickable="true" enabled="true"
visible-to-user="true" bounds="[32,1190][332,1320]"/>
<node text="白色" clickable="true" enabled="true"
visible-to-user="true" bounds="[359,1190][659,1320]"/>
<node text="尺码" bounds="[32,1500][114,1550]"/>
<node text="均码" clickable="true" enabled="true"
visible-to-user="true" bounds="[32,1580][391,1660]"/>
"""
else:
scroll_content = """
<node text="黑色 ¥6.93" clickable="true" enabled="true"
visible-to-user="true" bounds="[32,1095][332,1460]"/>
<node text="白色 ¥6.93" clickable="true" enabled="true"
visible-to-user="true" bounds="[359,1095][659,1460]"/>
<node text="尺码" bounds="[32,1605][114,1653]"/>
<node text="均码" clickable="true" selected="true"
enabled="true" visible-to-user="true"
bounds="[32,1677][391,1753]"/>
"""
return f"""<hierarchy>
<node package="com.xunmeng.pinduoduo" bounds="[0,0][1080,2376]">
<node text="确认款式" bounds="[430,650][650,710]"/>
<node text="请选择: 颜色分类" bounds="[360,770][990,830]"/>
<node class="android.widget.EditText" text="1"
bounds="[440,870][510,940]"/>
<node content-desc="减少数量" clickable="true"
bounds="[360,870][430,940]"/>
<node content-desc="增加数量" clickable="true"
bounds="[515,870][585,940]"/>
<node package="com.xunmeng.pinduoduo"
class="androidx.recyclerview.widget.RecyclerView"
scrollable="true" enabled="true" visible-to-user="true"
bounds="[0,1095][1020,1918]">
{scroll_content}
</node>
<node text="选择颜色分类后,提交订单"
bounds="[0,2181][1080,2328]"/>
</node>
</hierarchy>"""
class FakeEditor:
def __init__(self, device) -> None:
self.device = device
@@ -180,6 +227,32 @@ class FakeDevice:
raise AssertionError(f"未预期的 selector: {selector}")
class VerticallyScrolledColorPanelDevice(FakeDevice):
"""模拟 PDD 记住规格面板纵向位置。"""
def __init__(self, *, starts_at_top: bool, restores_on_swipe: bool = True):
super().__init__()
self.at_color_top = starts_at_top
self.restores_on_swipe = restores_on_swipe
self.swipes = []
def dump_hierarchy(self):
if not self.has_opened:
return super().dump_hierarchy()
if self.mode == "home":
return home_xml()
if self.mode == "panel":
return vertically_scrolled_purchase_panel_xml(
at_color_top=self.at_color_top
)
return super().dump_hierarchy()
def swipe(self, x1, y1, x2, y2, duration=0.0):
self.swipes.append((x1, y1, x2, y2, duration))
if self.restores_on_swipe:
self.at_color_top = True
class ColdStartDevice(FakeDevice):
def __init__(self) -> None:
super().__init__()
@@ -927,6 +1000,74 @@ class U2PddPurchaseAdapterTest(unittest.TestCase):
self.assertEqual(device.clicks[-1], (540, 2140))
adapter.close()
def test_purchase_restores_color_region_before_selecting_color(self):
clock = FakeClock()
device = VerticallyScrolledColorPanelDevice(starts_at_top=False)
selected_xml = []
def select_color_fn(_device, xml_data, _target, **_kwargs):
selected_xml.append(xml_data)
return True
adapter = self._live_adapter(
device,
[],
sleeper=clock.sleep,
monotonic=clock.monotonic,
select_color_fn=select_color_fn,
)
adapter.open_goods(GOODS_URL)
adapter.select_options({"color": "黑色"})
self.assertEqual(len(device.swipes), 1)
self.assertLess(device.swipes[0][1], device.swipes[0][3])
self.assertEqual(len(selected_xml), 1)
self.assertIn('text="颜色分类"', selected_xml[0])
adapter.close()
def test_purchase_does_not_scroll_when_color_region_is_already_visible(self):
device = VerticallyScrolledColorPanelDevice(starts_at_top=True)
adapter = self._live_adapter(
device,
[],
select_color_fn=lambda *_args, **_kwargs: True,
)
adapter.open_goods(GOODS_URL)
adapter.select_options({"color": "黑色"})
self.assertEqual(device.swipes, [])
adapter.close()
def test_purchase_stops_when_color_region_cannot_be_restored(self):
clock = FakeClock()
device = VerticallyScrolledColorPanelDevice(
starts_at_top=False,
restores_on_swipe=False,
)
select_calls = []
adapter = self._live_adapter(
device,
[],
sleeper=clock.sleep,
monotonic=clock.monotonic,
select_color_fn=lambda *_args, **_kwargs: select_calls.append(True),
)
adapter.open_goods(GOODS_URL)
with self.assertRaises(PddPurchaseError) as raised:
adapter.select_options({"color": "黑色"})
self.assertEqual(
raised.exception.code,
"PURCHASE_PANEL_COLOR_REGION_NOT_FOUND",
)
self.assertEqual(len(device.swipes), 2)
self.assertEqual(select_calls, [])
self.assertEqual(len(device.clicks), 1)
adapter.close()
def test_address_is_split_at_first_separator_before_tagging(self):
self.assertEqual(
_tagged_shipping_address(