fix(android): locate color images from raw cards (#133)
This commit is contained in:
@@ -11,8 +11,8 @@ android {
|
||||
applicationId = "cn.ilapage.goauto.agent"
|
||||
minSdk = 23
|
||||
targetSdk = 34
|
||||
versionCode = 31
|
||||
versionName = "0.9.18"
|
||||
versionCode = 32
|
||||
versionName = "0.9.19"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
|
||||
+15
-2
@@ -243,6 +243,7 @@ object PddScreenParser {
|
||||
val dimensions = buildList<VisibleDimension> {
|
||||
headings.forEachIndexed { index, heading ->
|
||||
val lower = headings.getOrNull(index + 1)?.bounds?.top ?: Int.MAX_VALUE
|
||||
val dimensionKey = dimensionKey(heading.label, config, any { it.key == "color" })
|
||||
val values = panelVisible.asSequence()
|
||||
.filter { it.clickable && it.bounds.top >= heading.bounds.bottom && it.bounds.bottom <= lower }
|
||||
.filter { it.bounds.width > 0 && it.bounds.height > 0 && it.label.length <= 80 }
|
||||
@@ -254,7 +255,7 @@ object PddScreenParser {
|
||||
it.label,
|
||||
it.enabled && !stateText.containsUnavailableWord(),
|
||||
it,
|
||||
colorImageBounds(it, visibleNodes),
|
||||
if (dimensionKey == "color") colorImageBoundsForOption(it, visibleNodes) else null,
|
||||
)
|
||||
}
|
||||
.toList()
|
||||
@@ -262,7 +263,7 @@ object PddScreenParser {
|
||||
.map { (_, sameLabel) ->
|
||||
sameLabel.minBy { option -> safeOptionRank(option.node, visibleNodes) }
|
||||
}
|
||||
if (values.isNotEmpty()) add(VisibleDimension(dimensionKey(heading.label, config, any { it.key == "color" }), heading.label, values))
|
||||
if (values.isNotEmpty()) add(VisibleDimension(dimensionKey, heading.label, values))
|
||||
}
|
||||
}
|
||||
// Opening evidence is intentionally independent from whether the current
|
||||
@@ -363,6 +364,18 @@ object PddScreenParser {
|
||||
val prefix = "${node.path}/"
|
||||
return nodes.filter { it.path.startsWith(prefix) }
|
||||
}
|
||||
private fun colorImageBoundsForOption(option: SnapshotNode, nodes: List<SnapshotNode>): NodeBounds? {
|
||||
val byPath = nodes.associateBy(SnapshotNode::path)
|
||||
val expectedLabel = option.label.replace(Regex("\\s+"), "").takeIf(String::isNotEmpty) ?: return null
|
||||
var candidate = byPath[option.path] ?: return null
|
||||
while (true) {
|
||||
val description = candidate.contentDescription?.replace(Regex("\\s+"), "")
|
||||
if (description == expectedLabel) {
|
||||
colorImageBounds(candidate, nodes)?.let { return it }
|
||||
}
|
||||
candidate = candidate.parentPath?.let(byPath::get) ?: return null
|
||||
}
|
||||
}
|
||||
|
||||
internal fun colorImageBounds(option: SnapshotNode, nodes: List<SnapshotNode>): NodeBounds? {
|
||||
val expected = option.contentDescription?.replace(Regex("\\s+"), "")?.takeIf(String::isNotEmpty) ?: return null
|
||||
|
||||
@@ -47,6 +47,78 @@ class PddProductDetailCollectorTest {
|
||||
|
||||
assertEquals(matching.bounds, PddScreenParser.colorImageBounds(card, listOf(card, matching, globalDuplicate, openLarge)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `full parser locates color image from original card while keeping caption click target`() {
|
||||
fun describedNode(
|
||||
path: String,
|
||||
parent: String?,
|
||||
text: String?,
|
||||
description: String?,
|
||||
bounds: NodeBounds,
|
||||
className: String,
|
||||
clickable: Boolean = false,
|
||||
scrollable: Boolean = false,
|
||||
) = SnapshotNode(
|
||||
path, parent, text, description, null, className, bounds,
|
||||
clickable, scrollable, selected = false, checked = false, enabled = true, visible = true,
|
||||
)
|
||||
|
||||
val targetBounds = NodeBounds(36, 500, 340, 620)
|
||||
val snapshot = UiSnapshot(
|
||||
PDD_PACKAGE,
|
||||
ACTIVITY,
|
||||
listOf(
|
||||
node("content", "", 0, 0, 1080, 2200, resourceId = "android:id/content", className = "android.widget.FrameLayout"),
|
||||
node("summary", "已选:", 20, 300, 700, 360),
|
||||
node("title", "确认款式", 20, 370, 300, 410),
|
||||
node("color-heading", "颜色分类", 20, 420, 300, 470),
|
||||
describedNode("scroll", null, null, null, NodeBounds(0, 400, 1080, 1800), "androidx.recyclerview.widget.RecyclerView", scrollable = true),
|
||||
describedNode("card", "scroll", null, " A色 ", NodeBounds(36, 500, 340, 650), "android.view.ViewGroup", clickable = true),
|
||||
describedNode("card/image", "card", null, " A色 ", targetBounds, "android.widget.ImageView", clickable = true),
|
||||
describedNode("card/big", "card", null, "打开大图", NodeBounds(280, 500, 340, 560), "android.widget.ImageView", clickable = true),
|
||||
describedNode("card/caption", "card", "A色", null, NodeBounds(36, 620, 340, 650), "android.widget.TextView", clickable = true),
|
||||
describedNode("other/image", "other", null, " A色 ", NodeBounds(382, 500, 698, 620), "android.widget.ImageView", clickable = true),
|
||||
node("size-heading", "尺码", 20, 700, 300, 750),
|
||||
node("size", "M", 36, 780, 220, 850, clickable = true),
|
||||
node("confirm", "确定", 0, 2000, 1080, 2150, clickable = true),
|
||||
),
|
||||
)
|
||||
|
||||
val parsed = PddScreenParser.parse(snapshot, config(), GOODS_ID, evidence())
|
||||
val color = parsed.dimensions.first { it.key == "color" }.values.single()
|
||||
val size = parsed.dimensions.first { it.key == "size" }.values.single()
|
||||
|
||||
assertEquals("card/caption", color.node.path)
|
||||
assertEquals(targetBounds, color.imageBounds)
|
||||
assertEquals(null, size.imageBounds)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `full parser leaves color image empty when original card has no description`() {
|
||||
val snapshot = UiSnapshot(
|
||||
PDD_PACKAGE,
|
||||
ACTIVITY,
|
||||
listOf(
|
||||
node("content", "", 0, 0, 1080, 2200, resourceId = "android:id/content", className = "android.widget.FrameLayout"),
|
||||
node("summary", "已选:", 20, 300, 700, 360),
|
||||
node("title", "确认款式", 20, 370, 300, 410),
|
||||
node("color-heading", "颜色分类", 20, 420, 300, 470),
|
||||
node("scroll", "", 0, 400, 1080, 1800, scrollable = true),
|
||||
node("card", "A色", 36, 500, 340, 650, clickable = true, parentPath = "scroll", className = "android.view.ViewGroup"),
|
||||
node("card/image", "A色", 36, 500, 340, 620, clickable = true, parentPath = "card", className = "android.widget.ImageView"),
|
||||
node("card/caption", "A色", 36, 620, 340, 650, clickable = true, parentPath = "card"),
|
||||
node("size-heading", "尺码", 20, 700, 300, 750),
|
||||
node("size", "M", 36, 780, 220, 850, clickable = true),
|
||||
node("confirm", "确定", 0, 2000, 1080, 2150, clickable = true),
|
||||
),
|
||||
)
|
||||
|
||||
val color = PddScreenParser.parse(snapshot, config(), GOODS_ID, evidence())
|
||||
.dimensions.first { it.key == "color" }.values.single()
|
||||
|
||||
assertEquals(null, color.imageBounds)
|
||||
}
|
||||
@Test
|
||||
fun pageLevelSoldOutRequiresExactTextAndNoNormalProductEvidence() {
|
||||
val soldOut = UiSnapshot(PDD_PACKAGE, ACTIVITY, listOf(
|
||||
|
||||
Reference in New Issue
Block a user