fix(runner): a selector the tree cannot resolve is not a focus failure

otherElementHoldsFocus answered true when FindNode returned nothing, so an
unresolvable target read as "another element holds focus". confirmFocus then
re-dumped, resolved nothing again, and errored unconditionally. Three of those
in a row abort the run.

Not knowing where the target is says nothing about where the text would land.
The guard's real case, a resolved target with focus outside its subtree, still
errors exactly as before.

Claude-Session: https://claude.ai/code/session_01A5KmftdEJ49A9z5mF5ESrX
This commit is contained in:
pj committed 2026-08-15 13:59:03 +05:30
1 parent c8e6c6634c
commit 231a74b876
2 files changed
+29 -2

No files matched your search

+5 -2
View File
@@ -827,13 +827,16 @@ func confirmFocus(
// otherElementHoldsFocus reports whether the hierarchy shows focus on
// something outside the selector's subtree, which is the state that sends
// typed text to the wrong field.
// typed text to the wrong field. A selector the hierarchy cannot resolve
// answers false: not knowing where the target is says nothing about where the
// text would land, and failing on it turns every step the dump has no node for
// into an apply error, which is an aborted run three steps later.
func otherElementHoldsFocus(tree *hierarchy.Tree, selector string) bool {
if tree == nil || focusedElement(tree) == nil {
return false
}
target := tree.FindNode(selector)
return target == nil || !holdsFocus(target)
return target != nil && !holdsFocus(target)
}
func focusedElement(tree *hierarchy.Tree) *hierarchy.Element {
+24
View File
@@ -974,6 +974,30 @@ func TestApplyAction_InputTextSkipsFocusCheckWhenNoOtherFieldHoldsFocus(t *testi
}
}
// A selector the hierarchy cannot resolve is the guard saying "I cannot tell",
// not "another element holds focus". Answering the second manufactures an apply
// error on every InputText the tree has no node for, and three in a row abort
// the run.
func TestApplyAction_InputTextTypesWhenTheTargetIsNotInTheHierarchy(t *testing.T) {
fastFocusSettle(t)
tree, err := hierarchy.Parse(loginFocusOnEmail)
if err != nil {
t.Fatalf("Parse: %v", err)
}
driverMock := mockdriver.New()
driverMock.HierarchyJSON = loginFocusOnEmail
action := verifier.Action{
Kind: verifier.ActionKindInputText,
On: "data-testid:LoginPassword",
Text: "ledger123",
}
mustDispatch(t, driverMock, action, tree)
if !typedText(driverMock.Actions(), "ledger123") {
t.Errorf("an unresolvable target must not block typing, got %v", driverMock.Actions())
}
}
func typedText(actions []mockdriver.Action, text string) bool {
for _, action := range actions {
if action.Kind == mockdriver.ActionInputText && action.Text == text {