fix(android): recognize clipped address cards (#193)
This commit is contained in:
+19
-17
@@ -314,22 +314,26 @@ class PurchaseLiveAutomation(
|
||||
}
|
||||
|
||||
private fun addressEntryTargets(snapshot: UiSnapshot, panelBounds: NodeBounds): List<SnapshotNode> {
|
||||
val candidates = snapshot.nodes.filter { node ->
|
||||
node.visible && node.enabled && MASKED_PHONE.containsMatchIn(node.label) && fullyInside(node.bounds, panelBounds)
|
||||
}.distinctBy { it.path }
|
||||
if (candidates.size <= 1) return candidates
|
||||
val byPath = snapshot.nodes.associateBy { it.path }
|
||||
val cards = candidates.map { source ->
|
||||
var ancestor = source.parentPath?.let(byPath::get)
|
||||
while (ancestor != null && !ancestor.clickable) ancestor = ancestor.parentPath?.let(byPath::get)
|
||||
ancestor?.path?.let { it to source }
|
||||
}
|
||||
if (cards.all { it != null }) {
|
||||
return cards.filterNotNull().groupBy({ it.first }, { it.second }).values.map { sources ->
|
||||
sources.minBy { it.bounds.width.toLong() * it.bounds.height }
|
||||
val cards = snapshot.nodes.filter { node ->
|
||||
node.visible && node.enabled && MASKED_PHONE.containsMatchIn(node.label)
|
||||
}.mapNotNull { source ->
|
||||
var target: SnapshotNode? = source
|
||||
while (target != null && !target.clickable) target = target.parentPath?.let(byPath::get)
|
||||
target?.takeIf { card ->
|
||||
card.visible && card.enabled && visiblyIntersects(card.bounds, panelBounds)
|
||||
}
|
||||
}
|
||||
return mergedDirect(candidates)
|
||||
}.distinctBy { it.path }
|
||||
return mergedDirect(cards)
|
||||
}
|
||||
|
||||
private fun visiblyIntersects(target: NodeBounds, scope: NodeBounds): Boolean {
|
||||
val overlapWidth = (minOf(target.right, scope.right) - maxOf(target.left, scope.left)).coerceAtLeast(0)
|
||||
val overlapHeight = (minOf(target.bottom, scope.bottom) - maxOf(target.top, scope.top)).coerceAtLeast(0)
|
||||
val comparableWidth = minOf(target.width, scope.width)
|
||||
return comparableWidth > 0 && target.height > 0 &&
|
||||
overlapWidth * 100 >= comparableWidth * 60 &&
|
||||
overlapHeight >= minOf(ADDRESS_CARD_MIN_VISIBLE_HEIGHT, target.height)
|
||||
}
|
||||
|
||||
private fun verifiedPurchaseSurfaceBounds(snapshot: UiSnapshot, input: PurchaseExecutionInput): NodeBounds? {
|
||||
@@ -372,9 +376,6 @@ class PurchaseLiveAutomation(
|
||||
private fun inside(child: NodeBounds, parent: NodeBounds): Boolean =
|
||||
child.centerX in parent.left..parent.right && child.centerY in parent.top..parent.bottom
|
||||
|
||||
private fun fullyInside(child: NodeBounds, parent: NodeBounds): Boolean =
|
||||
child.left >= parent.left && child.top >= parent.top && child.right <= parent.right && child.bottom <= parent.bottom
|
||||
|
||||
private fun uniqueClickable(snapshot: UiSnapshot, nodes: List<SnapshotNode>): List<SnapshotNode> {
|
||||
val byPath = snapshot.nodes.associateBy { it.path }
|
||||
return nodes.mapNotNull { source ->
|
||||
@@ -494,6 +495,7 @@ class PurchaseLiveAutomation(
|
||||
const val PDD_PACKAGE = "com.xunmeng.pinduoduo"
|
||||
const val WECHAT_PACKAGE = "com.tencent.mm"
|
||||
val MASKED_PHONE = Regex("(?<![0-9])[0-9]{3}\\*{4}[0-9]{4}(?![0-9])")
|
||||
const val ADDRESS_CARD_MIN_VISIBLE_HEIGHT = 24
|
||||
val FINAL_SUBMIT_MARKERS = listOf("提交订单", "现在买,仅", "确认购买")
|
||||
val PAYMENT_MARKERS = listOf("立即支付", "确认支付", "输入支付密码")
|
||||
val UNPAID_MARKERS = listOf("待付款", "待支付", "去支付")
|
||||
|
||||
@@ -325,13 +325,14 @@ class PurchaseLiveAutomationTest {
|
||||
|
||||
@Test
|
||||
fun `verified purchase surface pulls down when PDD exposes no scrollable panel`() {
|
||||
val driver = LiveDriver(addressClipped = true, panelInitiallyScrollable = false)
|
||||
val driver = LiveDriver(addressClipped = true, panelInitiallyScrollable = false, addressAtTopAfterReveal = true)
|
||||
val address = PurchaseLiveAutomation(driver, pause = {}).updateShippingAddress(input().copy(addressSuffix = "_cg56"))
|
||||
|
||||
assertEquals("广东省广州市天园街道骏景花园骏晖轩1202_cg56", address.expectedAddress)
|
||||
assertEquals(0, driver.scopedSwipes)
|
||||
assertEquals(1, driver.surfacePullDowns)
|
||||
assertEquals(1, driver.addressTaps)
|
||||
assertEquals("address-card", driver.lastAddressTapPath)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -412,6 +413,7 @@ class PurchaseLiveAutomationTest {
|
||||
private val panelInitiallyScrollable: Boolean = true,
|
||||
private val panelScrollableAfterReveal: Boolean = true,
|
||||
private val hideSelectedSummary: Boolean = false,
|
||||
private val addressAtTopAfterReveal: Boolean = false,
|
||||
private val chooserAfterSubmit: Boolean = false,
|
||||
private val trustedChooser: Boolean = true,
|
||||
private val splitConfirmationAddress: Boolean = false,
|
||||
@@ -436,6 +438,7 @@ class PurchaseLiveAutomationTest {
|
||||
var genericSwipes = 0
|
||||
var surfacePullDowns = 0
|
||||
var addressTaps = 0
|
||||
var lastAddressTapPath: String? = null
|
||||
var lastInputTargetPath: String? = null
|
||||
var backCount = 0
|
||||
var postSubmitBackCount = 0
|
||||
@@ -506,7 +509,14 @@ 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")
|
||||
}
|
||||
else -> nodes += node("phone", "138****5678", bounds = NodeBounds(20, 650, 300, 710))
|
||||
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")
|
||||
}
|
||||
else -> {
|
||||
nodes += node("address-card", "", clickable = true, bounds = NodeBounds(0, 620, 1080, 850))
|
||||
nodes += node("phone", "138****5678", bounds = NodeBounds(20, 650, 300, 710), parentPath = "address-card")
|
||||
}
|
||||
}
|
||||
val suffixStart = address.lastIndexOf("_cg")
|
||||
val addressBody = if (suffixStart >= 0) address.substring(0, suffixStart) else address
|
||||
@@ -550,8 +560,9 @@ class PurchaseLiveAutomationTest {
|
||||
|
||||
override fun tapPurchaseFresh(target: SnapshotNode): FreshActionResult {
|
||||
addressTaps++
|
||||
lastAddressTapPath = target.path
|
||||
clicked += target.label
|
||||
if (target.label == "138****5678") page = "panel"
|
||||
if (target.path.startsWith("address-card")) page = "panel"
|
||||
return FreshActionResult.SUCCESS
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user