package browser import ( "os" "regexp" "strings" "testing" "time" ) // TestIsWSL2 verifica que isWSL2 detecta el entorno correctamente leyendo /proc/version. func TestIsWSL2(t *testing.T) { b, err := os.ReadFile("/proc/version") if err != nil { t.Skip("/proc/version no disponible (no es Linux)") } lower := strings.ToLower(string(b)) expectWSL2 := strings.Contains(lower, "microsoft") || strings.Contains(lower, "wsl") got := isWSL2() if got != expectWSL2 { t.Errorf("isWSL2() = %v, want %v (contenido: %q)", got, expectWSL2, string(b)[:min(120, len(b))]) } t.Logf("isWSL2() = %v (entorno: %s)", got, string(b)[:min(80, len(b))]) } // TestTranslateUserDataDirForWindows verifica la traduccion de rutas Linux a Windows. // Solo corre si wslpath esta disponible (WSL2). func TestTranslateUserDataDirForWindows(t *testing.T) { if !isWSL2() { t.Skip("solo aplica en WSL2") } result, err := translateUserDataDirForWindows("/tmp/test-chrome-profile") if err != nil { t.Fatalf("translateUserDataDirForWindows: %v", err) } // El resultado debe contener backslash (ruta Windows) o empezar con [A-Z]: reWin := regexp.MustCompile(`(?i)^[A-Z]:\\|\\`) if !reWin.MatchString(result) { t.Errorf("resultado no parece ruta Windows: %q", result) } t.Logf("translateUserDataDirForWindows('/tmp/test-chrome-profile') = %q", result) } // TestIsWindowsExe verifica que isWindowsExe detecta ejecutables .exe. func TestIsWindowsExe(t *testing.T) { cases := []struct { path string want bool }{ {"/mnt/c/Program Files/Google/Chrome/Application/chrome.exe", true}, {"chrome.exe", true}, {"CHROME.EXE", true}, {"/usr/bin/google-chrome", false}, {"chromium", false}, {"/mnt/c/Windows/System32/cmd.exe", true}, } for _, c := range cases { got := isWindowsExe(c.path) if got != c.want { t.Errorf("isWindowsExe(%q) = %v, want %v", c.path, got, c.want) } } } // TestFindChrome verifica que el ejecutable de Chrome es localizable. func TestFindChrome(t *testing.T) { path, err := findChrome() if err != nil { t.Skipf("Chrome no encontrado (entorno sin Chrome): %v", err) } if path == "" { t.Error("findChrome retorno path vacio") } t.Logf("Chrome encontrado en: %s", path) } // TestChromeLaunchAndConnect lanza Chrome, conecta CDP, navega a about:blank y cierra. // Requiere CHROME_E2E=1 (integración real con Chrome). func TestChromeLaunchAndConnect(t *testing.T) { if os.Getenv("CHROME_E2E") != "1" { t.Skip("skip: requiere CHROME_E2E=1 y Chrome real") } // Verificar que Chrome esta disponible if _, err := findChrome(); err != nil { t.Skipf("Chrome no disponible: %v", err) } opts := ChromeLaunchOpts{ Port: 9223, // puerto alternativo para no interferir con sesiones existentes UserDataDir: t.TempDir(), Headless: true, } pid, err := ChromeLaunch(opts) if err != nil { t.Fatalf("ChromeLaunch: %v", err) } t.Logf("Chrome lanzado con PID %d en puerto %d", pid, opts.Port) defer func() { if err := CdpClose(nil, pid); err != nil { t.Logf("CdpClose (pid only): %v", err) } }() // Conectar CDP conn, err := CdpConnect(opts.Port) if err != nil { t.Fatalf("CdpConnect: %v", err) } defer func() { if err := CdpClose(conn, 0); err != nil { t.Logf("CdpClose (conn): %v", err) } }() // Navegar a about:blank if err := CdpNavigate(conn, "about:blank"); err != nil { t.Fatalf("CdpNavigate about:blank: %v", err) } t.Log("Navegacion a about:blank exitosa") } // TestCdpEvaluate ejecuta JS simple en Chrome y verifica el resultado. // Requiere CHROME_E2E=1. func TestCdpEvaluate(t *testing.T) { if os.Getenv("CHROME_E2E") != "1" { t.Skip("skip: requiere CHROME_E2E=1 y Chrome real") } if _, err := findChrome(); err != nil { t.Skipf("Chrome no disponible: %v", err) } opts := ChromeLaunchOpts{ Port: 9224, UserDataDir: t.TempDir(), Headless: true, } pid, err := ChromeLaunch(opts) if err != nil { t.Fatalf("ChromeLaunch: %v", err) } defer CdpClose(nil, pid) conn, err := CdpConnect(opts.Port) if err != nil { t.Fatalf("CdpConnect: %v", err) } defer CdpClose(conn, 0) if err := CdpNavigate(conn, "about:blank"); err != nil { t.Fatalf("CdpNavigate: %v", err) } t.Run("expresion aritmetica simple", func(t *testing.T) { result, err := CdpEvaluate(conn, "1 + 2") if err != nil { t.Fatalf("CdpEvaluate: %v", err) } if result != "3" { t.Errorf("esperado '3', got %q", result) } }) t.Run("string literal", func(t *testing.T) { result, err := CdpEvaluate(conn, `"hola mundo"`) if err != nil { t.Fatalf("CdpEvaluate: %v", err) } if result != "hola mundo" { t.Errorf("esperado 'hola mundo', got %q", result) } }) t.Run("typeof window", func(t *testing.T) { result, err := CdpEvaluate(conn, "typeof window") if err != nil { t.Fatalf("CdpEvaluate: %v", err) } if result != "object" { t.Errorf("esperado 'object', got %q", result) } }) } // TestCdpGetHTML obtiene el HTML de about:blank y verifica que contiene elementos basicos. // Requiere CHROME_E2E=1. func TestCdpGetHTML(t *testing.T) { if os.Getenv("CHROME_E2E") != "1" { t.Skip("skip: requiere CHROME_E2E=1 y Chrome real") } if _, err := findChrome(); err != nil { t.Skipf("Chrome no disponible: %v", err) } opts := ChromeLaunchOpts{ Port: 9225, UserDataDir: t.TempDir(), Headless: true, } pid, err := ChromeLaunch(opts) if err != nil { t.Fatalf("ChromeLaunch: %v", err) } defer CdpClose(nil, pid) conn, err := CdpConnect(opts.Port) if err != nil { t.Fatalf("CdpConnect: %v", err) } defer CdpClose(conn, 0) if err := CdpNavigate(conn, "about:blank"); err != nil { t.Fatalf("CdpNavigate: %v", err) } t.Run("html de about blank contiene html y body", func(t *testing.T) { html, err := CdpGetHTML(conn) if err != nil { t.Fatalf("CdpGetHTML: %v", err) } lower := strings.ToLower(html) if !strings.Contains(lower, ": %q", html[:min(200, len(html))]) } if !strings.Contains(lower, ": %q", html[:min(200, len(html))]) } t.Logf("HTML (primeros 200 chars): %s", html[:min(200, len(html))]) }) } // TestCdpScreenshot toma un screenshot de about:blank y verifica que se crea el archivo PNG. // Requiere CHROME_E2E=1. func TestCdpScreenshot(t *testing.T) { if os.Getenv("CHROME_E2E") != "1" { t.Skip("skip: requiere CHROME_E2E=1 y Chrome real") } if _, err := findChrome(); err != nil { t.Skipf("Chrome no disponible: %v", err) } opts := ChromeLaunchOpts{ Port: 9226, UserDataDir: t.TempDir(), Headless: true, } pid, err := ChromeLaunch(opts) if err != nil { t.Fatalf("ChromeLaunch: %v", err) } defer CdpClose(nil, pid) conn, err := CdpConnect(opts.Port) if err != nil { t.Fatalf("CdpConnect: %v", err) } defer CdpClose(conn, 0) if err := CdpNavigate(conn, "about:blank"); err != nil { t.Fatalf("CdpNavigate: %v", err) } // Esperar un momento para que la pagina cargue time.Sleep(500 * time.Millisecond) t.Run("screenshot png se crea correctamente", func(t *testing.T) { outputPath := t.TempDir() + "/screenshot.png" err := CdpScreenshot(conn, outputPath, CdpScreenshotOpts{FullPage: false}) if err != nil { t.Fatalf("CdpScreenshot: %v", err) } info, err := os.Stat(outputPath) if err != nil { t.Fatalf("screenshot no encontrado: %v", err) } if info.Size() == 0 { t.Error("screenshot vacio (0 bytes)") } t.Logf("Screenshot creado: %s (%d bytes)", outputPath, info.Size()) }) }