mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
refactor(verifier): one DecodeAction reads the unified flat wire contract
This commit is contained in:
1 parent
50cb9e98cb
commit
59cb77d29c
1 file changed
+52
-116
+52
-116
@@ -266,10 +266,10 @@ func selectorPathFromJS(runtime *goja.Runtime, arg goja.Value) ([]hierarchy.Sele
|
|||||||
}
|
}
|
||||||
|
|
||||||
// selectorStringFromJS returns a string representation of the selector argument
|
// selectorStringFromJS returns a string representation of the selector argument
|
||||||
// for tagging returned element objects (used by selectorOf to reconstruct the
|
// for tagging returned ax element objects (state.ax.find/findAll), so a spec
|
||||||
// selector when the element is passed back as an action target). Output
|
// reading an element's selector sees the canonical form. Output follows the
|
||||||
// follows the canonical hierarchy selector grammar: "k:v" pairs space-joined
|
// hierarchy selector grammar: "k:v" pairs space-joined per object, chains
|
||||||
// per object, chains joined by " > ".
|
// joined by " > ".
|
||||||
func selectorStringFromJS(runtime *goja.Runtime, arg goja.Value) string {
|
func selectorStringFromJS(runtime *goja.Runtime, arg goja.Value) string {
|
||||||
if arg == nil || goja.IsUndefined(arg) || goja.IsNull(arg) {
|
if arg == nil || goja.IsUndefined(arg) || goja.IsNull(arg) {
|
||||||
return ""
|
return ""
|
||||||
@@ -414,132 +414,68 @@ func jsonToJSValue(runtime *goja.Runtime, raw json.RawMessage) (goja.Value, erro
|
|||||||
return runtime.ToValue(generic), nil
|
return runtime.ToValue(generic), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// jsValueToAction converts a JS-side action object into a Go Action.
|
// wireAction is the unified flat wire contract both runtime entries emit
|
||||||
func jsValueToAction(runtime *goja.Runtime, value goja.Value) (Action, error) {
|
// (runtime-entry.ts serializeAction). ONE decoder reads it on both the goja
|
||||||
if value == nil || goja.IsNull(value) || goja.IsUndefined(value) {
|
// (native) and the runner (web) sides, so a field rename cannot silently turn
|
||||||
return Action{}, fmt.Errorf("nil action")
|
// an action into a no-op on one path only.
|
||||||
|
type wireAction struct {
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
X int `json:"x"`
|
||||||
|
Y int `json:"y"`
|
||||||
|
Selector string `json:"selector"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
Key string `json:"key"`
|
||||||
|
Direction string `json:"direction"`
|
||||||
|
FromX int `json:"fromX"`
|
||||||
|
FromY int `json:"fromY"`
|
||||||
|
ToX int `json:"toX"`
|
||||||
|
ToY int `json:"toY"`
|
||||||
|
DurationMillis int `json:"durationMillis"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeAction turns one serialized action (the flat camelCase wire contract)
|
||||||
|
// into a Go Action. An empty payload or JSON "null" reports ErrNoAction.
|
||||||
|
func DecodeAction(raw json.RawMessage) (Action, error) {
|
||||||
|
if len(raw) == 0 || string(raw) == "null" {
|
||||||
|
return Action{}, ErrNoAction
|
||||||
}
|
}
|
||||||
object := value.ToObject(runtime)
|
var wire wireAction
|
||||||
kindValue := object.Get("kind")
|
if err := json.Unmarshal(raw, &wire); err != nil {
|
||||||
if kindValue == nil {
|
return Action{}, fmt.Errorf("decode action: %w", err)
|
||||||
return Action{}, fmt.Errorf("action missing kind")
|
|
||||||
}
|
}
|
||||||
kind := kindValue.String()
|
switch wire.Kind {
|
||||||
switch kind {
|
|
||||||
case "Tap":
|
case "Tap":
|
||||||
on := object.Get("on")
|
return Action{Kind: ActionKindTap, On: wire.Selector, X: wire.X, Y: wire.Y}, nil
|
||||||
x, y := coordinatesOf(runtime, on)
|
|
||||||
return Action{Kind: ActionKindTap, On: selectorOf(runtime, on), X: x, Y: y}, nil
|
|
||||||
case "DoubleTap":
|
case "DoubleTap":
|
||||||
on := object.Get("on")
|
return Action{Kind: ActionKindDoubleTap, On: wire.Selector, X: wire.X, Y: wire.Y}, nil
|
||||||
x, y := coordinatesOf(runtime, on)
|
case "LongPress":
|
||||||
return Action{Kind: ActionKindDoubleTap, On: selectorOf(runtime, on), X: x, Y: y}, nil
|
return Action{Kind: ActionKindLongPress, On: wire.Selector, X: wire.X, Y: wire.Y}, nil
|
||||||
case "InputText":
|
case "InputText":
|
||||||
into := object.Get("into")
|
return Action{Kind: ActionKindInputText, On: wire.Selector, Text: wire.Text, X: wire.X, Y: wire.Y}, nil
|
||||||
text := object.Get("text")
|
|
||||||
x, y := coordinatesOf(runtime, into)
|
|
||||||
return Action{Kind: ActionKindInputText, On: selectorOf(runtime, into), Text: stringOf(text), X: x, Y: y}, nil
|
|
||||||
case "Swipe":
|
case "Swipe":
|
||||||
from := object.Get("from")
|
|
||||||
to := object.Get("to")
|
|
||||||
fromX, fromY := coordinatesOf(runtime, from)
|
|
||||||
toX, toY := coordinatesOf(runtime, to)
|
|
||||||
if fromX == 0 && fromY == 0 {
|
|
||||||
fromX, fromY = pointCoordinates(runtime, from)
|
|
||||||
}
|
|
||||||
if toX == 0 && toY == 0 {
|
|
||||||
toX, toY = pointCoordinates(runtime, to)
|
|
||||||
}
|
|
||||||
return Action{
|
return Action{
|
||||||
Kind: ActionKindSwipe,
|
Kind: ActionKindSwipe,
|
||||||
FromX: fromX,
|
FromX: wire.FromX,
|
||||||
FromY: fromY,
|
FromY: wire.FromY,
|
||||||
ToX: toX,
|
ToX: wire.ToX,
|
||||||
ToY: toY,
|
ToY: wire.ToY,
|
||||||
DurationMillis: intField(object, "durationMillis"),
|
DurationMillis: wire.DurationMillis,
|
||||||
}, nil
|
}, nil
|
||||||
case "LongPress":
|
|
||||||
on := object.Get("on")
|
|
||||||
x, y := coordinatesOf(runtime, on)
|
|
||||||
return Action{Kind: ActionKindLongPress, On: selectorOf(runtime, on), X: x, Y: y}, nil
|
|
||||||
case "Scroll":
|
case "Scroll":
|
||||||
in := object.Get("in")
|
|
||||||
x, y := coordinatesOf(runtime, in)
|
|
||||||
return Action{
|
return Action{
|
||||||
Kind: ActionKindScroll,
|
Kind: ActionKindScroll,
|
||||||
Direction: stringOf(object.Get("direction")),
|
Direction: wire.Direction,
|
||||||
On: selectorOf(runtime, in),
|
FromX: wire.FromX,
|
||||||
X: x,
|
FromY: wire.FromY,
|
||||||
Y: y,
|
ToX: wire.ToX,
|
||||||
|
ToY: wire.ToY,
|
||||||
|
DurationMillis: wire.DurationMillis,
|
||||||
}, nil
|
}, nil
|
||||||
case "PressKey":
|
case "PressKey":
|
||||||
return Action{Kind: ActionKindPressKey, Key: stringOf(object.Get("key"))}, nil
|
return Action{Kind: ActionKindPressKey, Key: wire.Key}, nil
|
||||||
case "Wait":
|
case "Wait":
|
||||||
return Action{Kind: ActionKindWait, DurationMillis: intField(object, "durationMillis")}, nil
|
return Action{Kind: ActionKindWait, DurationMillis: wire.DurationMillis}, nil
|
||||||
default:
|
default:
|
||||||
return Action{}, fmt.Errorf("unknown action kind %q", kind)
|
return Action{}, fmt.Errorf("unknown action kind %q", wire.Kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pointCoordinates reads a plain {x, y} literal (not an AX element), which is
|
|
||||||
// how Swipe endpoints are commonly expressed in specs.
|
|
||||||
func pointCoordinates(runtime *goja.Runtime, value goja.Value) (int, int) {
|
|
||||||
if value == nil || goja.IsNull(value) || goja.IsUndefined(value) {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
object := value.ToObject(runtime)
|
|
||||||
if object == nil {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
x := object.Get("x")
|
|
||||||
y := object.Get("y")
|
|
||||||
if x == nil || y == nil {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
return int(x.ToInteger()), int(y.ToInteger())
|
|
||||||
}
|
|
||||||
|
|
||||||
func intField(object *goja.Object, name string) int {
|
|
||||||
value := object.Get(name)
|
|
||||||
if value == nil || goja.IsUndefined(value) || goja.IsNull(value) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return int(value.ToInteger())
|
|
||||||
}
|
|
||||||
|
|
||||||
func selectorOf(runtime *goja.Runtime, value goja.Value) string {
|
|
||||||
if value == nil || goja.IsNull(value) || goja.IsUndefined(value) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
object := value.ToObject(runtime)
|
|
||||||
if object == nil {
|
|
||||||
return value.String()
|
|
||||||
}
|
|
||||||
if tag := object.Get(tagSelector); tag != nil && !goja.IsUndefined(tag) {
|
|
||||||
return tag.String()
|
|
||||||
}
|
|
||||||
return value.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func coordinatesOf(runtime *goja.Runtime, value goja.Value) (int, int) {
|
|
||||||
if value == nil || goja.IsNull(value) || goja.IsUndefined(value) {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
object := value.ToObject(runtime)
|
|
||||||
if object == nil {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
xValue := object.Get("x")
|
|
||||||
yValue := object.Get("y")
|
|
||||||
if xValue == nil || yValue == nil || goja.IsUndefined(xValue) || goja.IsUndefined(yValue) {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
return int(xValue.ToInteger()), int(yValue.ToInteger())
|
|
||||||
}
|
|
||||||
|
|
||||||
func stringOf(value goja.Value) string {
|
|
||||||
if value == nil || goja.IsNull(value) || goja.IsUndefined(value) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return value.String()
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user