From 15e2a008a71a4d4348965279a4abe26bc0dc125d Mon Sep 17 00:00:00 2001 From: QiuSW <105186638@qq.com> Date: Wed, 2 Sep 2026 09:33:27 +0800 Subject: [PATCH] fix(android): locate address above spec panel (#193) --- .../automation/PurchaseLiveAutomation.kt | 14 +++++++--- .../agent/PurchaseLiveAutomationTest.kt | 28 ++++++++++++++++--- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/android/app/src/main/java/cn/ilapage/goauto/agent/automation/PurchaseLiveAutomation.kt b/android/app/src/main/java/cn/ilapage/goauto/agent/automation/PurchaseLiveAutomation.kt index 1d4c370..da78af0 100644 --- a/android/app/src/main/java/cn/ilapage/goauto/agent/automation/PurchaseLiveAutomation.kt +++ b/android/app/src/main/java/cn/ilapage/goauto/agent/automation/PurchaseLiveAutomation.kt @@ -48,14 +48,19 @@ class PurchaseLiveAutomation( var previousSignature: String? = null var unchangedCount = 0 var verifiedPanelBounds: NodeBounds? = null + var trustedPurchaseSurfaceBounds: NodeBounds? = null for (attempt in 0 until 5) { pageProblem(snapshot) val panel = purchasePanelScrollTargets(snapshot) if (panel.size == 1) verifiedPanelBounds = panel.single().bounds - if (verifiedPanelBounds == null && input != null) { - verifiedPanelBounds = verifiedPurchaseSurfaceBounds(snapshot, input) + if (trustedPurchaseSurfaceBounds == null && input != null) { + trustedPurchaseSurfaceBounds = verifiedPurchaseSurfaceBounds(snapshot, input) } - val entries = verifiedPanelBounds?.let { bounds -> addressEntryTargets(snapshot, bounds) }.orEmpty() + val entries = mergedDirect( + listOfNotNull(trustedPurchaseSurfaceBounds, verifiedPanelBounds) + .distinct() + .flatMap { bounds -> addressEntryTargets(snapshot, bounds) }, + ) if (entries.size == 1) { when (driver.tapPurchaseFresh(entries.single())) { FreshActionResult.SUCCESS -> Unit @@ -68,7 +73,8 @@ class PurchaseLiveAutomation( } return editAndVerifyAddress(snapshot, addressSuffix) } - val fallbackBounds = if (panel.size == 1) panel.single().bounds else input?.let { verifiedPurchaseSurfaceBounds(snapshot, it) } + val fallbackBounds = if (panel.size == 1) panel.single().bounds else trustedPurchaseSurfaceBounds + ?: input?.let { verifiedPurchaseSurfaceBounds(snapshot, it) } ?: fail("PURCHASE_ADDRESS_ENTRY_AMBIGUOUS", "无法确认唯一的采购页面滑动区域,未创建订单") val signature = viewportSignature(snapshot, fallbackBounds) unchangedCount = if (signature == previousSignature) unchangedCount + 1 else 0 diff --git a/android/app/src/test/java/cn/ilapage/goauto/agent/PurchaseLiveAutomationTest.kt b/android/app/src/test/java/cn/ilapage/goauto/agent/PurchaseLiveAutomationTest.kt index 8107797..6e5d3ca 100644 --- a/android/app/src/test/java/cn/ilapage/goauto/agent/PurchaseLiveAutomationTest.kt +++ b/android/app/src/test/java/cn/ilapage/goauto/agent/PurchaseLiveAutomationTest.kt @@ -335,6 +335,18 @@ class PurchaseLiveAutomationTest { assertEquals("address-card", driver.lastAddressTapPath) } + @Test + fun `verified purchase surface finds address above a separate scrollable spec panel`() { + val driver = LiveDriver(addressClipped = true, addressAboveScrollablePanel = true) + val address = PurchaseLiveAutomation(driver, pause = {}).updateShippingAddress(input().copy(addressSuffix = "_cg58")) + + assertEquals("广东省广州市天园街道骏景花园骏晖轩1202_cg58", address.expectedAddress) + assertEquals(1, driver.scopedSwipes) + assertEquals(0, driver.surfacePullDowns) + assertEquals(1, driver.addressTaps) + assertEquals("address-card", driver.lastAddressTapPath) + } + @Test fun `verified purchase surface pulls down when PDD exposes multiple scrollable panels`() { val driver = LiveDriver(addressClipped = true, duplicatePanels = true) @@ -414,6 +426,7 @@ class PurchaseLiveAutomationTest { private val panelScrollableAfterReveal: Boolean = true, private val hideSelectedSummary: Boolean = false, private val addressAtTopAfterReveal: Boolean = false, + private val addressAboveScrollablePanel: Boolean = false, private val chooserAfterSubmit: Boolean = false, private val trustedChooser: Boolean = true, private val splitConfirmationAddress: Boolean = false, @@ -489,7 +502,12 @@ class PurchaseLiveAutomationTest { else -> { val nodes = mutableListOf( node("root", "", bounds = NodeBounds(0, 0, 1080, 2200)), - node("panel", "", scrollable = if (addressVisible) panelScrollableAfterReveal else panelInitiallyScrollable, bounds = NodeBounds(0, 400, 1080, 2100)), + node( + "panel", + "", + scrollable = if (addressVisible) panelScrollableAfterReveal else panelInitiallyScrollable, + bounds = if (addressAboveScrollablePanel) NodeBounds(0, 900, 1080, 2079) else NodeBounds(0, 400, 1080, 2100), + ), node("price", "¥20.00"), node("quantity", "2", className = "android.widget.EditText"), node("submit-parent", "", clickable = true), node("submit", "提交订单", parentPath = "submit-parent"), @@ -509,9 +527,11 @@ class PurchaseLiveAutomationTest { nodes += node("phone", "138****5678", bounds = NodeBounds(20, 650, 300, 710), parentPath = "address-card") nodes += node("phone-duplicate", "138****5678", bounds = NodeBounds(20, 735, 300, 795), parentPath = "address-card") } - addressAtTopAfterReveal -> { - nodes += node("address-card", "", clickable = true, bounds = NodeBounds(0, 300, 1080, 520)) - nodes += node("phone", "138****5678", bounds = NodeBounds(300, 330, 800, 380), parentPath = "address-card") + addressAtTopAfterReveal || addressAboveScrollablePanel -> { + val cardBounds = if (addressAboveScrollablePanel) NodeBounds(0, 366, 1080, 520) else NodeBounds(0, 300, 1080, 520) + val phoneBounds = if (addressAboveScrollablePanel) NodeBounds(412, 382, 993, 431) else NodeBounds(300, 330, 800, 380) + nodes += node("address-card", "", clickable = true, bounds = cardBounds) + nodes += node("phone", "138****5678", bounds = phoneBounds, parentPath = "address-card") } else -> { nodes += node("address-card", "", clickable = true, bounds = NodeBounds(0, 620, 1080, 850))