mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-04 12:07:09 +00:00
refactor(spec): one candidate producer over one target-eligibility rule
Both hosts routed verbs themselves and both policies enumerated their own actions, and all four drifted. Web sent `swipes` to scrollable containers only, so swipe-to-dismiss on a list row was reachable on native and unreachable on web; the model policy folded gestures its own way and could not reach what the seeded picker drew. A host now reports facts about every element and never decides which verb may act on it: targets.ts acceptsTarget owns that for both. pick.ts builtinCandidates is the single enumeration, and the model policy reads it through __sanderlingEnumerateBuiltin__ instead of reimplementing it in Go. Gesture verbs change with it: scrolls stay vertical over scrollable containers, swipes go free-form in all four directions from any element with real bounds. Claude-Session: https://claude.ai/code/session_01Fj4wJUikdABuMQEETwW55J
This commit is contained in:
1 parent
ef04a6de9d
commit
b5f64bf665
23 files changed
+1494
-594
No files matched your search
@@ -1,24 +1,45 @@
|
||||
package verifier
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/priyanshujain/sanderling/internal/hierarchy"
|
||||
)
|
||||
|
||||
// TestVerbAcceptsSwipeRequiresPositiveBounds locks the fix for the notification
|
||||
// shade: a zero-bounds element centers at (0,0), and a downward swipe from the
|
||||
// top-left corner is the system gesture that pulls the shade over the app. The
|
||||
// swipe verb must reject zero-bounds nodes like every other verb does.
|
||||
func TestVerbAcceptsSwipeRequiresPositiveBounds(t *testing.T) {
|
||||
zeroBounds := &hierarchy.Element{Bounds: hierarchy.Bounds{}}
|
||||
if verbAccepts("swipes", zeroBounds) {
|
||||
t.Error("swipes must reject a zero-bounds element (it centers at (0,0) and pulls the notification shade)")
|
||||
// TestTargetsReportFactsWithoutFiltering pins the native host's half of the
|
||||
// split introduced to stop the two hosts drifting: it reports what an element
|
||||
// IS and never decides which verb may act on it. The verb decision is one shared
|
||||
// rule (pkg/spec/src/targets.ts) both hosts consume, asserted across engines by
|
||||
// TestHostsAgreeOnTargetEligibility.
|
||||
func TestTargetsReportFactsWithoutFiltering(t *testing.T) {
|
||||
tree, err := hierarchy.Parse(hostParityTreeJSON)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
realBounds := &hierarchy.Element{Bounds: hierarchy.Bounds{Left: 100, Top: 400, Right: 980, Bottom: 600}}
|
||||
if !verbAccepts("swipes", realBounds) {
|
||||
t.Error("swipes must accept an element with positive bounds")
|
||||
v := &Verifier{lastTree: tree}
|
||||
targets := v.targets()
|
||||
if len(targets) != len(hostParityScreen) {
|
||||
t.Fatalf("targets() returned %d elements, want all %d in the tree",
|
||||
len(targets), len(hostParityScreen))
|
||||
}
|
||||
for index, target := range targets {
|
||||
want := hostParityScreen[index]
|
||||
got := []bool{
|
||||
target.clickable, target.enabled, target.editable, target.scrollable,
|
||||
}
|
||||
expected := []bool{
|
||||
want.clickable, want.enabled, want.editable, want.scrollable,
|
||||
}
|
||||
if !slices.Equal(got, expected) {
|
||||
t.Errorf("%s clickable/enabled/editable/scrollable = %v, want %v",
|
||||
want.name, got, expected)
|
||||
}
|
||||
positiveBounds := target.width > 0 && target.height > 0
|
||||
if positiveBounds != want.positiveBounds {
|
||||
t.Errorf("%s positive bounds = %v, want %v",
|
||||
want.name, positiveBounds, want.positiveBounds)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user