package engine import ( "context" "net/http" "net/http/httptest" "strings" "testing" ) func newTestServer(body string) *httptest.Server { return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") _, _ = w.Write([]byte(body)) })) } const testPage = ` Test Page

Hello World

Some content here.

` func TestLiteEngine_Navigate(t *testing.T) { ts := newTestServer(testPage) defer ts.Close() lite := NewLiteEngine() defer func() { _ = lite.Close() }() result, err := lite.Navigate(context.Background(), ts.URL) if err != nil { t.Fatalf("Navigate: %v", err) } if result.TabID == "" { t.Error("expected non-empty tabId") } if result.URL != ts.URL { t.Errorf("URL = %q, want %q", result.URL, ts.URL) } } func TestLiteEngine_Snapshot_All(t *testing.T) { ts := newTestServer(testPage) defer ts.Close() lite := NewLiteEngine() defer func() { _ = lite.Close() }() _, err := lite.Navigate(context.Background(), ts.URL) if err != nil { t.Fatalf("Navigate: %v", err) } nodes, err := lite.Snapshot(context.Background(), "", "all") if err != nil { t.Fatalf("Snapshot: %v", err) } if len(nodes) == 0 { t.Fatal("expected nodes in snapshot") } // Verify roles are assigned roleSet := make(map[string]bool) for _, n := range nodes { roleSet[n.Role] = true } for _, expected := range []string{"heading", "link", "button", "textbox", "combobox"} { if !roleSet[expected] { t.Errorf("expected role %q in snapshot, roles found: %v", expected, roleSet) } } } func TestLiteEngine_Snapshot_Interactive(t *testing.T) { ts := newTestServer(testPage) defer ts.Close() lite := NewLiteEngine() defer func() { _ = lite.Close() }() _, _ = lite.Navigate(context.Background(), ts.URL) nodes, err := lite.Snapshot(context.Background(), "", "interactive") if err != nil { t.Fatalf("Snapshot interactive: %v", err) } for _, n := range nodes { if !n.Interactive { t.Errorf("interactive filter returned non-interactive node: %+v", n) } } if len(nodes) < 3 { t.Errorf("expected at least 3 interactive nodes (link, button, input), got %d", len(nodes)) } } func TestLiteEngine_Text(t *testing.T) { ts := newTestServer(testPage) defer ts.Close() lite := NewLiteEngine() defer func() { _ = lite.Close() }() _, _ = lite.Navigate(context.Background(), ts.URL) text, err := lite.Text(context.Background(), "") if err != nil { t.Fatalf("Text: %v", err) } for _, want := range []string{"Hello World", "About", "Click Me", "Some content here"} { if !strings.Contains(text, want) { t.Errorf("text should contain %q, got: %s", want, text) } } } func TestLiteEngine_Click(t *testing.T) { ts := newTestServer(testPage) defer ts.Close() lite := NewLiteEngine() defer func() { _ = lite.Close() }() _, _ = lite.Navigate(context.Background(), ts.URL) nodes, _ := lite.Snapshot(context.Background(), "", "interactive") var buttonRef string for _, n := range nodes { if n.Role == "button" { buttonRef = n.Ref break } } if buttonRef == "" { t.Fatal("no button found in snapshot") } if err := lite.Click(context.Background(), "", buttonRef); err != nil { t.Errorf("Click: %v", err) } } func TestLiteEngine_Type(t *testing.T) { ts := newTestServer(testPage) defer ts.Close() lite := NewLiteEngine() defer func() { _ = lite.Close() }() _, _ = lite.Navigate(context.Background(), ts.URL) nodes, _ := lite.Snapshot(context.Background(), "", "interactive") var inputRef string for _, n := range nodes { if n.Role == "textbox" { inputRef = n.Ref break } } if inputRef == "" { t.Fatal("no textbox found in snapshot") } if err := lite.Type(context.Background(), "", inputRef, "hello"); err != nil { t.Errorf("Type: %v", err) } } func TestLiteEngine_RefNotFound(t *testing.T) { lite := NewLiteEngine() defer func() { _ = lite.Close() }() // No page loaded _, err := lite.Snapshot(context.Background(), "", "all") if err == nil { t.Error("expected error for snapshot without navigate") } // After navigate, bad ref ts := newTestServer(testPage) defer ts.Close() _, _ = lite.Navigate(context.Background(), ts.URL) _, _ = lite.Snapshot(context.Background(), "", "all") if err := lite.Click(context.Background(), "", "nonexistent"); err == nil { t.Error("expected error for bad ref") } } func TestLiteEngine_ScriptStyleSkipped(t *testing.T) { page := ` T

Visible

` ts := newTestServer(page) defer ts.Close() lite := NewLiteEngine() defer func() { _ = lite.Close() }() _, _ = lite.Navigate(context.Background(), ts.URL) nodes, _ := lite.Snapshot(context.Background(), "", "all") for _, n := range nodes { if n.Tag == "script" || n.Tag == "style" { t.Errorf("snapshot should skip %s elements", n.Tag) } } } func TestLiteEngine_AriaAttributes(t *testing.T) { page := ` Aria
content
Custom Btn ` ts := newTestServer(page) defer ts.Close() lite := NewLiteEngine() defer func() { _ = lite.Close() }() _, _ = lite.Navigate(context.Background(), ts.URL) nodes, _ := lite.Snapshot(context.Background(), "", "all") foundNav := false foundBtn := false for _, n := range nodes { if n.Role == "navigation" && n.Name == "Main Nav" { foundNav = true } if n.Role == "button" && n.Name == "Custom Btn" { foundBtn = true } } if !foundNav { t.Error("expected to find navigation role with aria-label") } if !foundBtn { t.Error("expected to find button role from role attribute") } } func TestLiteEngine_MultiTab(t *testing.T) { page1 := `Page 1

First

` page2 := `Page 2

Second

` ts1 := newTestServer(page1) defer ts1.Close() ts2 := newTestServer(page2) defer ts2.Close() lite := NewLiteEngine() defer func() { _ = lite.Close() }() res1, _ := lite.Navigate(context.Background(), ts1.URL) res2, _ := lite.Navigate(context.Background(), ts2.URL) if res1.TabID == res2.TabID { t.Error("tab IDs should be different") } // Current tab is the most recent (page2) text, _ := lite.Text(context.Background(), "") if !strings.Contains(text, "Second") { t.Errorf("expected page 2 text, got: %s", text) } text, _ = lite.Text(context.Background(), res1.TabID) if !strings.Contains(text, "First") { t.Errorf("expected page 1 text via tab ID, got: %s", text) } } func TestLiteEngine_Close(t *testing.T) { ts := newTestServer(testPage) defer ts.Close() lite := NewLiteEngine() _, _ = lite.Navigate(context.Background(), ts.URL) if err := lite.Close(); err != nil { t.Errorf("Close: %v", err) } // After close, operations should fail _, err := lite.Snapshot(context.Background(), "", "all") if err == nil { t.Error("expected error after close") } } func TestLiteEngine_Capabilities(t *testing.T) { lite := NewLiteEngine() defer func() { _ = lite.Close() }() caps := lite.Capabilities() if len(caps) != 5 { t.Errorf("expected 5 capabilities, got %d", len(caps)) } } func TestLiteEngine_Name(t *testing.T) { lite := NewLiteEngine() defer func() { _ = lite.Close() }() if lite.Name() != "lite" { t.Errorf("Name() = %q, want %q", lite.Name(), "lite") } } func TestNormalizeWhitespace(t *testing.T) { tests := []struct { in string want string }{ {"hello world", "hello world"}, {" hello world ", "hello world"}, {"line1\n\n\nline2", "line1 line2"}, {"\t tabs \t and \t spaces", "tabs and spaces"}, {"", ""}, } for _, tt := range tests { got := normalizeWhitespace(tt.in) if got != tt.want { t.Errorf("normalizeWhitespace(%q) = %q, want %q", tt.in, got, tt.want) } } }