Files
navegator/pkg/browser/browser_test.go
T
Developer 3253828fef
Tests / Lint (push) Has been cancelled
Tests / Unit Tests (push) Has been cancelled
Tests / E2E Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Initial commit: navegator - Chrome CDP automation for LLMs
Add complete navegator system for stealthy browser automation:
- CDP client with WebSocket communication
- Browser API with navigation, storage, network, runtime
- Stealth flags and anti-detection scripts
- Persistent profile support
- Examples and comprehensive documentation

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-03-24 23:33:07 +01:00

292 lines
6.8 KiB
Go

package browser
import (
"context"
"os"
"path/filepath"
"testing"
"time"
)
func TestLaunchBrowser(t *testing.T) {
ctx := context.Background()
// Crear directorio temporal para perfiles
tempDir := t.TempDir()
config := DefaultConfig()
config.ProfilesBaseDir = tempDir
config.ProfileName = "test-launch"
config.StealthFlags.Headless = true
config.StartTimeout = 15 * time.Second
// Lanzar navegador
b, err := Launch(ctx, config)
if err != nil {
t.Fatalf("Failed to launch browser: %v", err)
}
defer b.Close()
// Verificar que el perfil se creó
profilePath := filepath.Join(tempDir, "test-launch")
if _, err := os.Stat(profilePath); os.IsNotExist(err) {
t.Errorf("Profile directory not created: %s", profilePath)
}
// Verificar que tenemos debug URL
if b.DebugURL() == "" {
t.Error("Debug URL is empty")
}
// Verificar que tenemos target ID
if b.TargetID() == "" {
t.Error("Target ID is empty")
}
}
func TestNavigate(t *testing.T) {
ctx := context.Background()
tempDir := t.TempDir()
config := DefaultConfig()
config.ProfilesBaseDir = tempDir
config.ProfileName = "test-navigate"
config.StealthFlags.Headless = true
b, err := Launch(ctx, config)
if err != nil {
t.Fatalf("Failed to launch browser: %v", err)
}
defer b.Close()
// Navegar a example.com
opts := DefaultNavigateOptions()
opts.Timeout = 15 * time.Second
err = b.Navigate(ctx, "https://example.com", opts)
if err != nil {
t.Fatalf("Failed to navigate: %v", err)
}
// Verificar que estamos en la página correcta
result, err := b.Evaluate(ctx, "window.location.href")
if err != nil {
t.Fatalf("Failed to evaluate location: %v", err)
}
url, ok := result.Value.(string)
if !ok || url != "https://example.com/" {
t.Errorf("Expected URL https://example.com/, got %v", result.Value)
}
}
func TestScreenshot(t *testing.T) {
ctx := context.Background()
tempDir := t.TempDir()
config := DefaultConfig()
config.ProfilesBaseDir = tempDir
config.ProfileName = "test-screenshot"
config.StealthFlags.Headless = true
b, err := Launch(ctx, config)
if err != nil {
t.Fatalf("Failed to launch browser: %v", err)
}
defer b.Close()
// Navegar
opts := DefaultNavigateOptions()
opts.Timeout = 15 * time.Second
if err := b.Navigate(ctx, "https://example.com", opts); err != nil {
t.Logf("Navigation warning: %v", err)
}
// Tomar screenshot
screenshot, err := b.Screenshot(ctx, false)
if err != nil {
t.Fatalf("Failed to take screenshot: %v", err)
}
// Verificar que tiene contenido
if len(screenshot) == 0 {
t.Error("Screenshot is empty")
}
// Verificar que es PNG válido (empieza con magic bytes)
if len(screenshot) < 8 || screenshot[0] != 0x89 || screenshot[1] != 0x50 {
t.Error("Screenshot is not a valid PNG")
}
}
func TestEvaluate(t *testing.T) {
ctx := context.Background()
tempDir := t.TempDir()
config := DefaultConfig()
config.ProfilesBaseDir = tempDir
config.ProfileName = "test-evaluate"
config.StealthFlags.Headless = true
b, err := Launch(ctx, config)
if err != nil {
t.Fatalf("Failed to launch browser: %v", err)
}
defer b.Close()
// Navegar
opts := DefaultNavigateOptions()
opts.Timeout = 15 * time.Second
if err := b.Navigate(ctx, "https://example.com", opts); err != nil {
t.Logf("Navigation warning: %v", err)
}
// Evaluar JavaScript
result, err := b.Evaluate(ctx, "2 + 2")
if err != nil {
t.Fatalf("Failed to evaluate: %v", err)
}
// Verificar resultado
val, ok := result.Value.(float64)
if !ok || val != 4 {
t.Errorf("Expected 4, got %v", result.Value)
}
}
func TestStealthFlags(t *testing.T) {
ctx := context.Background()
tempDir := t.TempDir()
config := DefaultConfig()
config.ProfilesBaseDir = tempDir
config.ProfileName = "test-stealth"
config.StealthFlags.Headless = true
b, err := Launch(ctx, config)
if err != nil {
t.Fatalf("Failed to launch browser: %v", err)
}
defer b.Close()
// Navegar
opts := DefaultNavigateOptions()
opts.Timeout = 15 * time.Second
if err := b.Navigate(ctx, "https://example.com", opts); err != nil {
t.Logf("Navigation warning: %v", err)
}
// Verificar navigator.webdriver = false
result, err := b.Evaluate(ctx, "navigator.webdriver")
if err != nil {
t.Fatalf("Failed to evaluate webdriver: %v", err)
}
// Debe ser false o undefined
if result.Value != false && result.Value != nil {
t.Errorf("navigator.webdriver should be false, got %v", result.Value)
}
// Verificar window.chrome existe
chromeExists, err := b.Evaluate(ctx, "typeof window.chrome !== 'undefined'")
if err != nil {
t.Fatalf("Failed to check chrome object: %v", err)
}
if chromeExists.Value != true {
t.Error("window.chrome should exist")
}
}
func TestRecorder(t *testing.T) {
tempDir := t.TempDir()
recordFile := filepath.Join(tempDir, "test-recording.log")
recorder, err := NewRecorder(recordFile)
if err != nil {
t.Fatalf("Failed to create recorder: %v", err)
}
defer recorder.Close()
// Registrar acción
recorder.Record("TestAction", map[string]interface{}{
"param1": "value1",
"param2": 123,
}, "test result", nil)
recorder.Close()
// Verificar que el archivo existe y tiene contenido
content, err := os.ReadFile(recordFile)
if err != nil {
t.Fatalf("Failed to read recording file: %v", err)
}
if len(content) == 0 {
t.Error("Recording file is empty")
}
// Verificar que contiene JSON
contentStr := string(content)
if !contains(contentStr, "TestAction") {
t.Error("Recording doesn't contain action name")
}
}
func TestProfilePersistence(t *testing.T) {
ctx := context.Background()
tempDir := t.TempDir()
profileName := "test-persistence"
// Primera sesión: crear perfil
config1 := DefaultConfig()
config1.ProfilesBaseDir = tempDir
config1.ProfileName = profileName
config1.StealthFlags.Headless = true
b1, err := Launch(ctx, config1)
if err != nil {
t.Fatalf("Failed to launch browser (session 1): %v", err)
}
// Verificar perfil creado
profilePath := filepath.Join(tempDir, profileName)
if _, err := os.Stat(profilePath); os.IsNotExist(err) {
t.Fatalf("Profile not created: %s", profilePath)
}
b1.Close()
// Segunda sesión: reutilizar perfil
config2 := DefaultConfig()
config2.ProfilesBaseDir = tempDir
config2.ProfileName = profileName
config2.StealthFlags.Headless = true
b2, err := Launch(ctx, config2)
if err != nil {
t.Fatalf("Failed to launch browser (session 2): %v", err)
}
defer b2.Close()
// Perfil debe seguir existiendo
if _, err := os.Stat(profilePath); os.IsNotExist(err) {
t.Error("Profile was deleted between sessions")
}
}
// Helper function
func contains(s, substr string) bool {
return len(s) >= len(substr) && (s == substr || len(s) > len(substr) && containsHelper(s, substr))
}
func containsHelper(s, substr string) bool {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return true
}
}
return false
}