fix: address the Maps search box by id, and re-prove Phase 5
The Maps omnibox hint rotates: it read "Search here" in the Phase 5 run and "Try petrol stations, ATMs" in the AUD-128 re-verification, so the recipe's text match on it failed. The resource id does not rotate. - internal/ui: add Find/FindID and the "id:" selector; Find dispatches an id: selector to the resource id and everything else to text as before. - internal/recipe: wait/tap/wait_gone/tap_if and the tap helper use Find, so a recipe may name a resource id. Guards stay text-only. - recipes/maps_navigate.recipe: wait/tap id:search_omnibox_text_box. - tests for the id selector; docs and README updated. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -103,16 +103,56 @@ func shortID(id string) string {
|
||||
return id
|
||||
}
|
||||
|
||||
// IDSelector marks a selector that matches a resource id rather than text. A
|
||||
// step that names "id:search_omnibox_text_box" finds the element with that
|
||||
// resource id, whatever its visible label happens to be.
|
||||
//
|
||||
// This exists because some labels are not stable: the Google Maps search box
|
||||
// carries a rotating hint as its content description ("Search here", "Try
|
||||
// petrol stations, ATMs", ...), so a recipe that matched that text broke when
|
||||
// the phone picked a different hint. The resource id does not rotate.
|
||||
const IDSelector = "id:"
|
||||
|
||||
// FindText returns nodes whose text or content description matches query.
|
||||
// When exact is false the match is case-insensitive and substring-based.
|
||||
func (h *Hierarchy) FindText(query string, exact bool) []*Node {
|
||||
return h.find(query, exact, false)
|
||||
}
|
||||
|
||||
// FindID returns nodes whose resource id matches query. When exact is false the
|
||||
// match is case-insensitive and substring-based, so the short name
|
||||
// "search_omnibox_text_box" matches the full
|
||||
// "com.google.android.apps.maps:id/search_omnibox_text_box".
|
||||
func (h *Hierarchy) FindID(query string, exact bool) []*Node {
|
||||
return h.find(query, exact, true)
|
||||
}
|
||||
|
||||
// Find returns nodes matching a recipe selector. A selector that starts with
|
||||
// "id:" is matched against the resource id; any other selector is matched
|
||||
// against text and content description (FR-4).
|
||||
func (h *Hierarchy) Find(query string, exact bool) []*Node {
|
||||
if id, ok := strings.CutPrefix(query, IDSelector); ok {
|
||||
return h.FindID(id, exact)
|
||||
}
|
||||
return h.FindText(query, exact)
|
||||
}
|
||||
|
||||
func (h *Hierarchy) find(query string, exact, byID bool) []*Node {
|
||||
if query == "" {
|
||||
return nil
|
||||
}
|
||||
needle := strings.ToLower(query)
|
||||
var out []*Node
|
||||
h.Walk(func(n *Node) {
|
||||
for _, candidate := range []string{n.Text, n.ContentDesc} {
|
||||
var candidates []string
|
||||
if byID {
|
||||
if n.ResourceID != "" {
|
||||
candidates = []string{shortID(n.ResourceID), n.ResourceID}
|
||||
}
|
||||
} else {
|
||||
candidates = []string{n.Text, n.ContentDesc}
|
||||
}
|
||||
for _, candidate := range candidates {
|
||||
if candidate == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user