fix: 兼容地址保存系统浮层 (#180)
This commit is contained in:
@@ -314,6 +314,20 @@ def address_edit_xml(address: str) -> str:
|
||||
</hierarchy>"""
|
||||
|
||||
|
||||
def coloros_password_overlay_xml() -> str:
|
||||
return """<hierarchy>
|
||||
<node package="com.xunmeng.pinduoduo" class="android.widget.FrameLayout"
|
||||
bounds="[0,0][1080,2376]"/>
|
||||
<node package="com.coloros.codebook" class="android.widget.FrameLayout"
|
||||
clickable="true" focusable="true" focused="true"
|
||||
visible-to-user="true" bounds="[0,0][1080,2328]">
|
||||
<node package="com.coloros.codebook" text="系统提示"
|
||||
class="android.widget.TextView" visible-to-user="true"
|
||||
bounds="[96,1965][516,2022]"/>
|
||||
</node>
|
||||
</hierarchy>"""
|
||||
|
||||
|
||||
class FakeAddressEditor:
|
||||
def __init__(self, device, matched: bool) -> None:
|
||||
self.device = device
|
||||
@@ -363,6 +377,82 @@ class AddressFlowDevice(FakeDevice):
|
||||
return super().__call__(**selector)
|
||||
|
||||
|
||||
class AddressSaveOverlayDevice(AddressFlowDevice):
|
||||
"""模拟保存后出现 ColorOS 密码本浮层。"""
|
||||
|
||||
def __init__(self, clear_after_reads=None) -> None:
|
||||
super().__init__()
|
||||
self.clear_after_reads = clear_after_reads
|
||||
self.overlay_reads = 0
|
||||
self.save_clicks = 0
|
||||
|
||||
def dump_hierarchy(self):
|
||||
if self.mode == "address_overlay":
|
||||
self.overlay_reads += 1
|
||||
if (
|
||||
self.clear_after_reads is not None
|
||||
and self.overlay_reads > self.clear_after_reads
|
||||
):
|
||||
self.mode = "address_saved"
|
||||
return super().dump_hierarchy()
|
||||
return coloros_password_overlay_xml()
|
||||
return super().dump_hierarchy()
|
||||
|
||||
def click(self, x, y):
|
||||
if self.mode == "address_edit":
|
||||
self.clicks.append((x, y))
|
||||
self.save_clicks += 1
|
||||
self.mode = "address_overlay"
|
||||
return
|
||||
super().click(x, y)
|
||||
|
||||
def press(self, key):
|
||||
self.presses.append(key)
|
||||
if self.mode == "address_overlay" and key == "back":
|
||||
self.mode = "address_saved"
|
||||
|
||||
|
||||
class PersistentAddressSaveOverlayDevice(AddressSaveOverlayDevice):
|
||||
"""模拟系统返回键也没有关闭密码本浮层。"""
|
||||
|
||||
def press(self, key):
|
||||
self.presses.append(key)
|
||||
|
||||
|
||||
class UnknownAfterAddressSaveDevice(AddressFlowDevice):
|
||||
"""模拟保存后进入非白名单未知浮层。"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.save_clicks = 0
|
||||
|
||||
def dump_hierarchy(self):
|
||||
if self.mode == "address_save_unknown":
|
||||
return """<hierarchy>
|
||||
<node package="com.example.unknown" text="未知页面"
|
||||
focused="true" visible-to-user="true"
|
||||
bounds="[0,0][1080,2376]"/>
|
||||
</hierarchy>"""
|
||||
return super().dump_hierarchy()
|
||||
|
||||
def click(self, x, y):
|
||||
if self.mode == "address_edit":
|
||||
self.clicks.append((x, y))
|
||||
self.save_clicks += 1
|
||||
self.mode = "address_save_unknown"
|
||||
return
|
||||
super().click(x, y)
|
||||
|
||||
|
||||
class HiddenSavedAddressDevice(AddressFlowDevice):
|
||||
"""模拟地址面板不在控件树中暴露完整地址。"""
|
||||
|
||||
def dump_hierarchy(self):
|
||||
if self.mode == "address_saved":
|
||||
return address_panel_xml("")
|
||||
return super().dump_hierarchy()
|
||||
|
||||
|
||||
class LostFirstModifyClickAddressDevice(AddressFlowDevice):
|
||||
"""模拟第一次“修改”点击被页面吞掉。"""
|
||||
|
||||
@@ -593,7 +683,7 @@ class U2PddPurchaseAdapterTest(unittest.TestCase):
|
||||
self.assertEqual(device.clicks[-1], (540, 2140))
|
||||
adapter.close()
|
||||
|
||||
def test_program_address_suffix_is_replaced_without_cutting_body(self):
|
||||
def test_address_is_split_at_first_separator_before_tagging(self):
|
||||
self.assertEqual(
|
||||
_tagged_shipping_address(
|
||||
"测试省测试市测试区测试路1号-1dd212", "cg2"
|
||||
@@ -610,7 +700,13 @@ class U2PddPurchaseAdapterTest(unittest.TestCase):
|
||||
_tagged_shipping_address(
|
||||
"测试省-开发区测试路1号", "cg4"
|
||||
),
|
||||
"测试省-开发区测试路1号_cg4",
|
||||
"测试省_cg4",
|
||||
)
|
||||
self.assertEqual(
|
||||
_tagged_shipping_address(
|
||||
"测试省测试市测试路1号_旧采购编号", "cg4"
|
||||
),
|
||||
"测试省测试市测试路1号_cg4",
|
||||
)
|
||||
|
||||
def test_live_adapter_updates_address_and_returns_to_confirmation(self):
|
||||
@@ -634,6 +730,105 @@ class U2PddPurchaseAdapterTest(unittest.TestCase):
|
||||
self.assertEqual(state.price_cent, 503)
|
||||
adapter.close()
|
||||
|
||||
def test_saved_address_panel_can_hide_full_address(self):
|
||||
device = HiddenSavedAddressDevice()
|
||||
adapter = self._live_adapter(device, [])
|
||||
adapter.open_goods(GOODS_URL)
|
||||
adapter.select_options(
|
||||
{"color": "黑色", "size": "3XL【140-165斤】"}
|
||||
)
|
||||
|
||||
adapter.update_shipping_address("cg2")
|
||||
|
||||
self.assertEqual(device.mode, "panel")
|
||||
self.assertEqual(device.presses, [])
|
||||
adapter.close()
|
||||
|
||||
def test_coloros_overlay_can_disappear_without_system_back(self):
|
||||
device = AddressSaveOverlayDevice(clear_after_reads=2)
|
||||
adapter = self._live_adapter(device, [])
|
||||
adapter.open_goods(GOODS_URL)
|
||||
adapter.select_options(
|
||||
{"color": "黑色", "size": "3XL【140-165斤】"}
|
||||
)
|
||||
|
||||
adapter.update_shipping_address("cg2")
|
||||
|
||||
self.assertEqual(device.save_clicks, 1)
|
||||
self.assertEqual(device.presses, [])
|
||||
self.assertEqual(device.mode, "panel")
|
||||
adapter.close()
|
||||
|
||||
def test_persistent_coloros_overlay_is_dismissed_with_one_system_back(self):
|
||||
device = AddressSaveOverlayDevice()
|
||||
adapter = self._live_adapter(device, [])
|
||||
adapter.open_goods(GOODS_URL)
|
||||
adapter.select_options(
|
||||
{"color": "黑色", "size": "3XL【140-165斤】"}
|
||||
)
|
||||
|
||||
adapter.update_shipping_address("cg2")
|
||||
|
||||
self.assertEqual(device.save_clicks, 1)
|
||||
self.assertEqual(device.presses, ["back"])
|
||||
self.assertEqual(device.mode, "panel")
|
||||
adapter.close()
|
||||
|
||||
def test_coloros_overlay_is_never_dismissed_more_than_once(self):
|
||||
clock = FakeClock()
|
||||
device = PersistentAddressSaveOverlayDevice()
|
||||
adapter = self._live_adapter(
|
||||
device,
|
||||
[],
|
||||
sleeper=clock.sleep,
|
||||
monotonic=clock.monotonic,
|
||||
panel_timeout=1.0,
|
||||
)
|
||||
adapter.open_goods(GOODS_URL)
|
||||
adapter.select_options(
|
||||
{"color": "黑色", "size": "3XL【140-165斤】"}
|
||||
)
|
||||
|
||||
with self.assertRaises(PddPurchaseError) as raised:
|
||||
adapter.update_shipping_address("cg2")
|
||||
|
||||
self.assertEqual(raised.exception.code, "PURCHASE_ADDRESS_PAGE_TIMEOUT")
|
||||
self.assertTrue(
|
||||
raised.exception.diagnostics["coloros_password_overlay_seen"]
|
||||
)
|
||||
self.assertEqual(raised.exception.diagnostics["system_back_attempts"], 1)
|
||||
self.assertEqual(device.save_clicks, 1)
|
||||
self.assertEqual(device.presses, ["back"])
|
||||
adapter.close()
|
||||
|
||||
def test_unknown_page_after_save_does_not_press_back_or_click_again(self):
|
||||
clock = FakeClock()
|
||||
device = UnknownAfterAddressSaveDevice()
|
||||
adapter = self._live_adapter(
|
||||
device,
|
||||
[],
|
||||
sleeper=clock.sleep,
|
||||
monotonic=clock.monotonic,
|
||||
panel_timeout=1.0,
|
||||
)
|
||||
adapter.open_goods(GOODS_URL)
|
||||
adapter.select_options(
|
||||
{"color": "黑色", "size": "3XL【140-165斤】"}
|
||||
)
|
||||
|
||||
with self.assertRaises(PddPurchaseError) as raised:
|
||||
adapter.update_shipping_address("cg2")
|
||||
|
||||
self.assertEqual(raised.exception.code, "PURCHASE_ADDRESS_PAGE_TIMEOUT")
|
||||
self.assertEqual(raised.exception.diagnostics["observed_page"], "unknown")
|
||||
self.assertFalse(
|
||||
raised.exception.diagnostics["coloros_password_overlay_seen"]
|
||||
)
|
||||
self.assertEqual(raised.exception.diagnostics["system_back_attempts"], 0)
|
||||
self.assertEqual(device.save_clicks, 1)
|
||||
self.assertEqual(device.presses, [])
|
||||
adapter.close()
|
||||
|
||||
def test_address_edit_retries_once_when_first_modify_click_is_lost(self):
|
||||
device = LostFirstModifyClickAddressDevice()
|
||||
adapter = self._live_adapter(device, [])
|
||||
|
||||
Reference in New Issue
Block a user