8742cb25be
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
123 lines
4.0 KiB
Go
123 lines
4.0 KiB
Go
package browser
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// TestCdpWaitIdleDefaults verifica el comportamiento observable de CdpWaitIdle
|
|
// sin requerir una instancia Chrome real.
|
|
func TestCdpWaitIdleDefaults(t *testing.T) {
|
|
t.Run("conexion nula retorna error inmediato", func(t *testing.T) {
|
|
err := CdpWaitIdle(nil, CdpWaitIdleOpts{})
|
|
if err == nil {
|
|
t.Fatal("esperaba error para conexion nula, got nil")
|
|
}
|
|
})
|
|
|
|
t.Run("opts con ceros aplica defaults antes de usar", func(t *testing.T) {
|
|
// Zero-value de CdpWaitIdleOpts debe tener todos los campos en 0
|
|
// para que la logica de defaults sea alcanzable.
|
|
var opts CdpWaitIdleOpts
|
|
if opts.QuietMs != 0 || opts.Timeout != 0 || opts.MaxInflight != 0 || opts.PollMs != 0 {
|
|
t.Fatal("zero-value de CdpWaitIdleOpts debe tener todos los campos en 0")
|
|
}
|
|
})
|
|
|
|
t.Run("mensaje de error nil-conn menciona cdp wait idle", func(t *testing.T) {
|
|
err := CdpWaitIdle(nil, CdpWaitIdleOpts{
|
|
QuietMs: 100,
|
|
Timeout: 500 * time.Millisecond,
|
|
PollMs: 50,
|
|
})
|
|
if err == nil {
|
|
t.Fatal("esperaba error, got nil")
|
|
}
|
|
if !strings.Contains(err.Error(), "cdp wait idle") {
|
|
t.Errorf("mensaje de error %q no contiene 'cdp wait idle'", err.Error())
|
|
}
|
|
})
|
|
}
|
|
|
|
// TestInflightTracker cubre el nucleo puro del contador de red. No requiere Chrome:
|
|
// alimenta secuencias de eventos {requestId, resourceType} y verifica el conteo.
|
|
func TestInflightTracker(t *testing.T) {
|
|
t.Run("golden: carga normal llega a idle", func(t *testing.T) {
|
|
tr := NewInflightTracker()
|
|
tr.OnRequest("r1", "Document")
|
|
tr.OnRequest("r2", "Script")
|
|
tr.OnRequest("r3", "Image")
|
|
if got := tr.Inflight(); got != 3 {
|
|
t.Fatalf("inflight tras 3 requests = %d, esperaba 3", got)
|
|
}
|
|
tr.OnFinish("r1")
|
|
tr.OnFinish("r2")
|
|
tr.OnFail("r3") // un recurso que falla tambien deja de estar en vuelo
|
|
if got := tr.Inflight(); got != 0 {
|
|
t.Fatalf("inflight tras completar todo = %d, esperaba 0", got)
|
|
}
|
|
if !tr.IsIdle(0) {
|
|
t.Error("esperaba IsIdle(0)=true con inflight=0")
|
|
}
|
|
})
|
|
|
|
t.Run("edge: analytics residual idle ok con MaxInflight=2", func(t *testing.T) {
|
|
tr := NewInflightTracker()
|
|
// La pagina cargo, pero 2 beacons de analytics quedan sin finalizar.
|
|
tr.OnRequest("doc", "Document")
|
|
tr.OnFinish("doc")
|
|
tr.OnRequest("beacon1", "Ping")
|
|
tr.OnRequest("beacon2", "XHR")
|
|
if got := tr.Inflight(); got != 2 {
|
|
t.Fatalf("inflight = %d, esperaba 2 (beacons residuales)", got)
|
|
}
|
|
if tr.IsIdle(0) {
|
|
t.Error("con MaxInflight=0 NO deberia ser idle (2 beacons en vuelo)")
|
|
}
|
|
if !tr.IsIdle(2) {
|
|
t.Error("con MaxInflight=2 (default) SI deberia ser idle")
|
|
}
|
|
})
|
|
|
|
t.Run("error/regresion: WebSocket abierto NO impide idle", func(t *testing.T) {
|
|
tr := NewInflightTracker()
|
|
tr.OnRequest("doc", "Document")
|
|
tr.OnFinish("doc")
|
|
// Un stream WebSocket se abre y nunca emite loadingFinished.
|
|
tr.OnRequest("ws1", "WebSocket")
|
|
// Un EventSource (SSE) tampoco termina.
|
|
tr.OnRequest("sse1", "EventSource")
|
|
if got := tr.Inflight(); got != 0 {
|
|
t.Fatalf("inflight = %d, esperaba 0 (WS/SSE excluidos)", got)
|
|
}
|
|
if !tr.IsIdle(0) {
|
|
t.Error("con WS+SSE abiertos pero excluidos, deberia ser idle absoluto")
|
|
}
|
|
})
|
|
|
|
t.Run("edge: finish de request no trackeado es no-op (no va negativo)", func(t *testing.T) {
|
|
tr := NewInflightTracker()
|
|
// loadingFinished de un requestId que nunca contamos (p.ej. el handshake
|
|
// de un WebSocket excluido) no debe romper el conteo.
|
|
tr.OnFinish("desconocido")
|
|
tr.OnFail("ws-handshake")
|
|
if got := tr.Inflight(); got != 0 {
|
|
t.Fatalf("inflight = %d, esperaba 0 (no negativo)", got)
|
|
}
|
|
tr.OnRequest("r1", "Fetch")
|
|
if got := tr.Inflight(); got != 1 {
|
|
t.Fatalf("inflight tras un request real = %d, esperaba 1", got)
|
|
}
|
|
})
|
|
|
|
t.Run("edge: requestId duplicado no infla el conteo", func(t *testing.T) {
|
|
tr := NewInflightTracker()
|
|
tr.OnRequest("r1", "Fetch")
|
|
tr.OnRequest("r1", "Fetch") // mismo id (redirect re-emite)
|
|
if got := tr.Inflight(); got != 1 {
|
|
t.Fatalf("inflight = %d, esperaba 1 (id deduplicado)", got)
|
|
}
|
|
})
|
|
}
|