diff --git a/internal/driver/chrome/driver_test.go b/internal/driver/chrome/driver_test.go index a554bf0..1997544 100644 --- a/internal/driver/chrome/driver_test.go +++ b/internal/driver/chrome/driver_test.go @@ -2,10 +2,71 @@ package chrome import ( "context" + "encoding/json" "testing" "time" ) +// TestHierarchy_EditableFlag confirms the injected hierarchy script marks text +// inputs, textareas, and contenteditable elements editable while leaving +// buttons and non-text inputs alone. +func TestHierarchy_EditableFlag(t *testing.T) { + const html = `` + + `` + + `` + + `` + + `
x
` + + `` + + `` + + d := New() + defer d.Terminate(context.Background()) + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + if err := d.Launch(ctx, "data:text/html,"+html, false, nil); err != nil { + t.Fatalf("Launch: %v", err) + } + dump, err := d.Hierarchy(ctx) + if err != nil { + t.Fatalf("Hierarchy: %v", err) + } + + type node struct { + Attributes map[string]string `json:"attributes"` + Children []node `json:"children"` + Editable *bool `json:"editable"` + } + var root node + if err := json.Unmarshal([]byte(dump), &root); err != nil { + t.Fatalf("unmarshal hierarchy: %v", err) + } + editableByID := map[string]*bool{} + var walk func(n node) + walk = func(n node) { + if id := n.Attributes["resource-id"]; id != "" { + editableByID[id] = n.Editable + } + for _, c := range n.Children { + walk(c) + } + } + walk(root) + + isEditable := func(id string) bool { + return editableByID[id] != nil && *editableByID[id] + } + for _, id := range []string{"name", "bio", "rich"} { + if !isEditable(id) { + t.Errorf("%q: editable = %v, want true", id, editableByID[id]) + } + } + for _, id := range []string{"go", "chk"} { + if isEditable(id) { + t.Errorf("%q: editable = true, want false/absent", id) + } + } +} + // TestRunCtx_CallerCancelPropagates confirms that cancelling the caller's // context cancels the chromedp-bound context returned by runCtx. This is the // channel by which step deadlines and Ctrl-C reach in-flight CDP calls.