fix(android): reopen PDD for manual purchase retries (#226)
This commit is contained in:
@@ -11,8 +11,8 @@ android {
|
||||
applicationId = "cn.ilapage.goauto.agent"
|
||||
minSdk = 23
|
||||
targetSdk = 34
|
||||
versionCode = 60
|
||||
versionName = "0.9.47"
|
||||
versionCode = 61
|
||||
versionName = "0.9.48"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
|
||||
+14
-4
@@ -83,10 +83,15 @@ class PurchaseRehearsalExecutor(
|
||||
val specSelectionProofs = mutableMapOf<String, ExactSpecSelectionProof>()
|
||||
val live = PurchaseLiveAutomation(driver, pause)
|
||||
for (action in rule.actions) {
|
||||
// spec_probe already opened this task's URL and the server reserves
|
||||
// the device while matching. Skip the whole navigation action,
|
||||
// including its configured wait/swipe hooks, in phase two.
|
||||
if (input.phase == "purchase" && action.type == PurchaseActionType.OPEN_PRODUCT) continue
|
||||
// The immediate phase-two handoff can reuse the PDD page retained by
|
||||
// spec_probe. A later manual retry may start from Agent (or another
|
||||
// unrelated screen), so only skip navigation when a fresh snapshot
|
||||
// still carries safe PDD product/spec evidence.
|
||||
if (
|
||||
input.phase == "purchase" &&
|
||||
action.type == PurchaseActionType.OPEN_PRODUCT &&
|
||||
canReuseCurrentProduct(input)
|
||||
) continue
|
||||
stepChanged(action.type.wireName)
|
||||
val failure = when (action.type) {
|
||||
PurchaseActionType.OPEN_PRODUCT -> openProduct(input, action)
|
||||
@@ -157,6 +162,11 @@ class PurchaseRehearsalExecutor(
|
||||
else failure("PURCHASE_RULE_INVALID", "正式采购规则缺少核单动作")
|
||||
}
|
||||
|
||||
private fun canReuseCurrentProduct(input: PurchaseExecutionInput): Boolean =
|
||||
currentScreen(input).let { screen ->
|
||||
screen.problem == null && screen.hasPurchaseProductEvidence()
|
||||
}
|
||||
|
||||
private fun validateBeforeDeviceAction(
|
||||
input: PurchaseExecutionInput,
|
||||
rule: PurchaseRule,
|
||||
|
||||
@@ -89,6 +89,57 @@ class PurchaseRehearsalExecutorTest {
|
||||
assertFalse(pauses.contains(700))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `manual purchase retry from agent reopens the task product url`() {
|
||||
val driver = FakePurchaseDriver(initiallyInAgent = true)
|
||||
var openCount = 0
|
||||
val outcome = PurchaseRehearsalExecutor(
|
||||
driver,
|
||||
openLink = {
|
||||
openCount++
|
||||
driver.leaveAgentAndOpenBrowser()
|
||||
true
|
||||
},
|
||||
probeSpecs = { null },
|
||||
pause = {},
|
||||
).execute(input(), PurchaseRuleParser.parse(rule()), PurchaseAgentCapabilities.supported)
|
||||
|
||||
assertEquals("rehearsal_completed", outcome.resultType)
|
||||
assertEquals(1, openCount)
|
||||
assertEquals(1, driver.openClickCount)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `purchase phase reopens when pdd foreground has no product evidence`() {
|
||||
val driver = FakePurchaseDriver(loadingPddCaptures = 1)
|
||||
var openCount = 0
|
||||
val outcome = PurchaseRehearsalExecutor(
|
||||
driver,
|
||||
openLink = { openCount++; driver.browser = true; true },
|
||||
probeSpecs = { null },
|
||||
pause = {},
|
||||
).execute(input(), PurchaseRuleParser.parse(rule()), PurchaseAgentCapabilities.supported)
|
||||
|
||||
assertEquals("rehearsal_completed", outcome.resultType)
|
||||
assertEquals(1, openCount)
|
||||
assertEquals(1, driver.openClickCount)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `purchase phase does not reuse a pdd login page`() {
|
||||
val driver = FakePurchaseDriver(pddProblemLabels = listOf("手机号登录", "登录后继续"))
|
||||
var openCount = 0
|
||||
val outcome = PurchaseRehearsalExecutor(
|
||||
driver,
|
||||
openLink = { openCount++; driver.browser = true; true },
|
||||
probeSpecs = { null },
|
||||
pause = {},
|
||||
).execute(input(), PurchaseRuleParser.parse(rule()), PurchaseAgentCapabilities.supported)
|
||||
|
||||
assertEquals("PDD_LOGIN_REQUIRED", outcome.errorCode)
|
||||
assertEquals(1, openCount)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `price range failure reports the observed unit price`() {
|
||||
val driver = FakePurchaseDriver()
|
||||
@@ -622,14 +673,16 @@ class PurchaseRehearsalExecutorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify product waits for product evidence instead of a visible loading frame`() {
|
||||
fun `purchase reopens and waits for product evidence instead of a visible loading frame`() {
|
||||
val pauses = mutableListOf<Long>()
|
||||
val driver = FakePurchaseDriver(loadingPddCaptures = 1)
|
||||
val outcome = PurchaseRehearsalExecutor(driver, { driver.browser = true; true }, { null }, pause = pauses::add)
|
||||
.execute(input(), PurchaseRuleParser.parse(rule()), PurchaseAgentCapabilities.supported)
|
||||
|
||||
assertEquals("rehearsal_completed", outcome.resultType)
|
||||
assertEquals(2, pauses.count { it == 100L })
|
||||
// Two stable reads belong to reopening the product; verifyProduct then
|
||||
// independently requires its second stable read before continuing.
|
||||
assertEquals(3, pauses.count { it == 100L })
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -994,8 +1047,10 @@ class PurchaseRehearsalExecutorTest {
|
||||
private val nonScrollablePanel: Boolean = false,
|
||||
private val unrecognizedPanel: Boolean = false,
|
||||
private val purchaseSwipeSucceeds: Boolean = true,
|
||||
initiallyInAgent: Boolean = false,
|
||||
) : PurchaseUiDriver {
|
||||
var browser = false
|
||||
private var inAgent = initiallyInAgent
|
||||
var panel = false
|
||||
var color: String? = null
|
||||
var size: String? = initialSize
|
||||
@@ -1019,6 +1074,9 @@ class PurchaseRehearsalExecutorTest {
|
||||
val clickedPaths = mutableListOf<String>()
|
||||
|
||||
override fun capture(): UiSnapshot {
|
||||
if (inAgent) {
|
||||
return UiSnapshot("cn.ilapage.goauto.agent", "MainActivity", listOf(node("content", "", 0, 0, 1080, 2200)))
|
||||
}
|
||||
if (browser && !panel && color == null && size == null) {
|
||||
browserCaptureCount++
|
||||
if (browserCaptureCount <= browserOpenVisibleAfterCaptures) {
|
||||
@@ -1145,6 +1203,11 @@ class PurchaseRehearsalExecutorTest {
|
||||
return UiSnapshot(PDD, ACTIVITY, nodes)
|
||||
}
|
||||
|
||||
fun leaveAgentAndOpenBrowser() {
|
||||
inAgent = false
|
||||
browser = true
|
||||
}
|
||||
|
||||
override fun clickFresh(target: SnapshotNode): FreshActionResult {
|
||||
clicked += target.label
|
||||
clickedPaths += target.path
|
||||
|
||||
Reference in New Issue
Block a user