6ad82167bb
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
291 lines
7.4 KiB
Go
291 lines
7.4 KiB
Go
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, "<html") {
|
|
t.Errorf("HTML no contiene tag <html>: %q", html[:min(200, len(html))])
|
|
}
|
|
if !strings.Contains(lower, "<body") {
|
|
t.Errorf("HTML no contiene tag <body>: %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())
|
|
})
|
|
}
|