fix: 安全采集图片颜色并取消默认尺码 (#160)

This commit is contained in:
chengma
2026-08-11 16:08:51 +08:00
parent 20950eff11
commit 2679106aab
2 changed files with 343 additions and 1 deletions
+169
View File
@@ -300,6 +300,95 @@ class DelayedHorizontalSwipeDevice(SnakeColorDevice):
self.sizes_visible = True
class ImageColorDevice(SnakeColorDevice):
"""图片型颜色卡片:点图片会进入大图,点底部文字才选中颜色。"""
def __init__(self, home_xml):
super().__init__(home_xml)
self.big_image_open = False
def dump_hierarchy(self):
if self.big_image_open:
return """<hierarchy>
<node class="android.support.v4.view.ViewPager"
scrollable="true" bounds="[0,0][1080,2000]">
<node class="android.widget.ImageView" content-desc="A色" />
</node>
<node class="android.widget.TextView" text="1/31"
bounds="[500,1850][580,1900]" />
</hierarchy>"""
return super().dump_hierarchy()
def click(self, x, y):
if self.panel_open and 740 <= y < 820:
self.clicks.append((x, y))
self.big_image_open = True
return
super().click(x, y)
def press(self, key):
if key == "back":
self.big_image_open = False
def _spec_xml(self):
xml_data = super()._spec_xml()
old = (
'<node class="android.view.ViewGroup" content-desc="A色" '
f'clickable="true" selected="{str(self.selected_color == "A色").lower()}" '
'bounds="[36,740][340,850]" visible-to-user="true" enabled="true" />'
)
new = (
'<node class="android.view.ViewGroup" content-desc="A色" '
f'clickable="true" selected="{str(self.selected_color == "A色").lower()}" '
'bounds="[36,740][340,850]" visible-to-user="true" enabled="true">'
'<node class="android.widget.ImageView" content-desc="A色" '
'clickable="true" bounds="[36,740][340,820]" />'
'<node class="android.widget.ImageView" content-desc="打开大图" '
'clickable="true" bounds="[280,740][340,800]" />'
'<node class="android.widget.TextView" text="A色" clickable="true" '
'bounds="[36,820][340,850]" visible-to-user="true" />'
'</node>'
)
return xml_data.replace(old, new)
class DefaultSizeDevice(SnakeColorDevice):
"""尺码在下方且默认选中,取消后才能完整遍历颜色。"""
def __init__(self, home_xml, *, can_clear=True):
super().__init__(home_xml)
self.size_selected = True
self.can_clear = can_clear
self.size_clicks = 0
def click(self, x, y):
if self.panel_open and self.sizes_visible and 1210 <= y <= 1300:
self.clicks.append((x, y))
self.size_clicks += 1
if self.can_clear:
self.size_selected = False
return
super().click(x, y)
def swipe(self, x1, y1, x2, y2, duration=0.35):
self.swipes.append(((x1, y1, x2, y2), {"duration": duration}))
self.swipe_panel_states.append(self.panel_open)
if abs(x2 - x1) > abs(y2 - y1):
self.page = 1 if x1 > x2 else 0
else:
self.sizes_visible = y1 > y2
def _spec_xml(self):
xml_data = super()._spec_xml()
if self.sizes_visible:
selected = str(self.size_selected).lower()
xml_data = xml_data.replace(
'content-desc="M"\n clickable="true"',
f'content-desc="M"\n clickable="true" selected="{selected}"',
)
return xml_data
class FakeClock:
"""测试用时钟:sleep 只推进虚拟时间,不真的等待。"""
@@ -822,6 +911,86 @@ class PddCollectParserTest(unittest.TestCase):
for item in data["skus"]
))
def test_image_color_card_clicks_caption_instead_of_opening_big_image(self):
device = ImageColorDevice(self.home_xml)
device.panel_open = True
device.page = 0
service = PddCollectService(
PddDeviceService(lambda _serial: device),
"USB-001",
"client-001",
sleeper=lambda _seconds: None,
)
sample = service._click_and_sample_color(device, "A色")
self.assertEqual(1000, sample.price_cent)
self.assertFalse(device.big_image_open)
self.assertGreaterEqual(device.clicks[0][1], 820)
def test_big_image_recovery_presses_back_only_once(self):
device = ImageColorDevice(self.home_xml)
device.panel_open = True
device.big_image_open = True
press_calls = []
original_press = device.press
def record_press(key):
press_calls.append(key)
original_press(key)
device.press = record_press
service = PddCollectService(
PddDeviceService(lambda _serial: device),
"USB-001",
"client-001",
sleeper=lambda _seconds: None,
)
service._recover_spec_panel_from_big_image(device)
self.assertEqual(["back"], press_calls)
self.assertFalse(device.big_image_open)
def test_default_size_is_cleared_once_before_colors_and_sizes_are_collected(self):
device = DefaultSizeDevice(self.home_xml)
service = PddCollectService(
PddDeviceService(lambda _serial: device),
"USB-001",
"client-001",
sleeper=lambda _seconds: None,
)
data = service.collect(
FakeTask("https://mobile.yangkeduo.com/goods.html?goods_id=123")
).to_pdd_data()
self.assertEqual(1, device.size_clicks)
self.assertFalse(device.size_selected)
self.assertEqual(
["A色", "B色", "C色", "F色", "E色", "D色"],
device.clicked_colors,
)
self.assertEqual(
["M", "L"],
[item["text"] for item in data["dimensions"][1]["values"]],
)
def test_size_that_cannot_be_cleared_is_not_clicked_repeatedly(self):
device = DefaultSizeDevice(self.home_xml, can_clear=False)
device.panel_open = True
service = PddCollectService(
PddDeviceService(lambda _serial: device),
"USB-001",
"client-001",
sleeper=lambda _seconds: None,
)
service._clear_default_size_selection(device)
self.assertEqual(1, device.size_clicks)
self.assertTrue(device.size_selected)
def test_visible_click_is_fallback_when_page_exposes_no_selection_state(self):
xml_data = keep_only_one_sku(self.spec_xml)
root = ET.fromstring(xml_data)