feat: load N13 UIA dump fixture
This commit is contained in:
109
internal/uiaselector/dump.go
Normal file
109
internal/uiaselector/dump.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package uiaselector
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type DumpResponse struct {
|
||||
OK bool `json:"ok"`
|
||||
Data *DumpData `json:"data"`
|
||||
Error any `json:"error"`
|
||||
}
|
||||
|
||||
type DumpData struct {
|
||||
Root *Node `json:"root"`
|
||||
}
|
||||
|
||||
type Node struct {
|
||||
Name string `json:"name"`
|
||||
ControlType string `json:"control_type"`
|
||||
AutomationID string `json:"automation_id"`
|
||||
ClassName string `json:"class_name"`
|
||||
FrameworkID string `json:"framework_id"`
|
||||
IsEnabled *bool `json:"is_enabled"`
|
||||
IsOffscreen *bool `json:"is_offscreen"`
|
||||
Bounds any `json:"bounds"`
|
||||
Children []Node `json:"children"`
|
||||
}
|
||||
|
||||
func LoadDumpFile(path string) (*Node, error) {
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
content = normalizeDumpJSON(content)
|
||||
var response DumpResponse
|
||||
if err := json.Unmarshal(content, &response); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !response.OK {
|
||||
return nil, fmt.Errorf("dump response ok=false: %v", response.Error)
|
||||
}
|
||||
if response.Data == nil || response.Data.Root == nil {
|
||||
return nil, fmt.Errorf("dump response has no data.root")
|
||||
}
|
||||
return response.Data.Root, nil
|
||||
}
|
||||
|
||||
func CountNodes(root *Node) int {
|
||||
if root == nil {
|
||||
return 0
|
||||
}
|
||||
count := 1
|
||||
for i := range root.Children {
|
||||
count += CountNodes(&root.Children[i])
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func normalizeDumpJSON(content []byte) []byte {
|
||||
content = bytes.TrimPrefix(content, []byte{0xEF, 0xBB, 0xBF})
|
||||
source := string(content)
|
||||
var out strings.Builder
|
||||
out.Grow(len(source))
|
||||
|
||||
inString := false
|
||||
escaped := false
|
||||
for i := 0; i < len(source); {
|
||||
ch := source[i]
|
||||
if inString {
|
||||
out.WriteByte(ch)
|
||||
if escaped {
|
||||
escaped = false
|
||||
} else if ch == '\\' {
|
||||
escaped = true
|
||||
} else if ch == '"' {
|
||||
inString = false
|
||||
}
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
if ch == '"' {
|
||||
inString = true
|
||||
out.WriteByte(ch)
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(source[i:], "-Infinity"):
|
||||
out.WriteString("null")
|
||||
i += len("-Infinity")
|
||||
case strings.HasPrefix(source[i:], "Infinity"):
|
||||
out.WriteString("null")
|
||||
i += len("Infinity")
|
||||
case strings.HasPrefix(source[i:], "NaN"):
|
||||
out.WriteString("null")
|
||||
i += len("NaN")
|
||||
default:
|
||||
out.WriteByte(ch)
|
||||
i++
|
||||
}
|
||||
}
|
||||
return []byte(out.String())
|
||||
}
|
||||
Reference in New Issue
Block a user