Files
navegator/dev/issues/007-alert-prompt-confirm-handling.md
T
Developer c165f2f788 docs: issues técnicas para nuevas funcionalidades
Agrega 19 issues técnicas documentando funcionalidades implementadas y pendientes.

Issues completadas (movidas a dev/issues/completed/):
- 001-conversor-web-markdown.md
- 002-accessibility-tree.md
- 003-gestion-cookies-perfil.md
- 004-gestion-extensiones-chrome.md
- 005-eliminar-timeouts-innecesarios.md

Issues implementadas:
- 006-manejo-tabs-ventanas.md
- 016-manejo-iframes.md
- 017-actions-api.md
- 018-file-uploads.md
- 019-expected-conditions-mejoradas.md

Issues pendientes (media prioridad):
- 007-alert-prompt-confirm-handling.md
- 008-screenshot-elementos-especificos.md
- 009-pdf-generation.md
- 010-device-emulation-completo.md
- 011-downloads-handling.md

Issues pendientes (baja prioridad / avanzado):
- 012-browser-contexts-multi-sesion.md
- 013-video-recording.md
- 014-network-mocking-avanzado.md
- 015-geolocation-permissions.md

Incluye también dev/NUEVAS_FUNCIONALIDADES.md con resumen completo.

Directorio: dev/
2026-03-25 00:49:06 +01:00

7.8 KiB

Issue #007: Alert/Prompt/Confirm Handling

Tipo: Enhancement Prioridad: Media Estado: Pendiente

Descripción

Implementar manejo de JavaScript dialogs (alert, prompt, confirm) que aparecen en páginas web.

Funcionalidad deseada

Tipos de dialogs

  • Alert: window.alert("mensaje") - Solo botón OK
  • Confirm: window.confirm("¿Continuar?") - OK/Cancel, retorna boolean
  • Prompt: window.prompt("Nombre:", "default") - Input + OK/Cancel

Operaciones

  • Detectar cuando aparece un dialog
  • Aceptar dialog (OK)
  • Rechazar dialog (Cancel)
  • Enviar texto a prompt
  • Obtener mensaje del dialog
  • Manejar dialogs automáticamente con reglas

Implementación técnica

Archivo sugerido

pkg/browser/dialogs.go

CDP Domain

Page.javascriptDialogOpening - Evento cuando aparece dialog Page.handleJavaScriptDialog - Responder al dialog

API propuesta

// DialogType tipo de dialog JavaScript
type DialogType string

const (
    DialogTypeAlert   DialogType = "alert"
    DialogTypeConfirm DialogType = "confirm"
    DialogTypePrompt  DialogType = "prompt"
)

// DialogAction acción a tomar con el dialog
type DialogAction string

const (
    DialogAccept  DialogAction = "accept"  // OK
    DialogDismiss DialogAction = "dismiss" // Cancel
)

// Dialog representa un dialog JavaScript
type Dialog struct {
    Type    DialogType
    Message string
    DefaultPromptText string
}

// HandleDialog maneja un dialog JavaScript cuando aparece
func (b *Browser) HandleDialog(ctx context.Context, action DialogAction, promptText string) error

// OnDialog registra un handler para dialogs
func (b *Browser) OnDialog(handler func(*Dialog) (DialogAction, string)) error

// WaitForDialog espera a que aparezca un dialog
func (b *Browser) WaitForDialog(ctx context.Context) (*Dialog, error)

// AcceptDialog acepta el próximo dialog que aparezca
func (b *Browser) AcceptDialog(ctx context.Context) error

// DismissDialog rechaza el próximo dialog que aparezca
func (b *Browser) DismissDialog(ctx context.Context) error

// PromptDialog responde a un prompt con texto
func (b *Browser) PromptDialog(ctx context.Context, text string) error

// AutoHandleDialogs configura manejo automático de dialogs
func (b *Browser) AutoHandleDialogs(ctx context.Context, action DialogAction) error

Casos de uso

Caso 1: Aceptar alert automáticamente

// Configurar manejo automático
b.AutoHandleDialogs(ctx, browser.DialogAccept)

// Cualquier alert será aceptado automáticamente
b.Click(ctx, "#trigger-alert")

Caso 2: Manejar confirm con lógica

b.OnDialog(func(dialog *browser.Dialog) (browser.DialogAction, string) {
    log.Printf("Dialog: %s - %s", dialog.Type, dialog.Message)

    if dialog.Type == browser.DialogTypeConfirm {
        if strings.Contains(dialog.Message, "eliminar") {
            return browser.DialogDismiss, "" // Cancelar eliminación
        }
    }

    return browser.DialogAccept, ""
})

b.Click(ctx, "#delete-button")

Caso 3: Responder a prompt

// Esperar prompt y responder
go func() {
    dialog, _ := b.WaitForDialog(ctx)
    if dialog.Type == browser.DialogTypePrompt {
        b.PromptDialog(ctx, "Mi nombre")
    }
}()

b.Click(ctx, "#ask-name-button")

Caso 4: Aceptar dialog específico

// Preparar handler antes de la acción
b.AcceptDialog(ctx)

// Acción que genera dialog
b.Click(ctx, "#show-alert")

Comandos CDP necesarios

// 1. Habilitar eventos de dialog
{"method": "Page.enable"}

// 2. Escuchar evento de dialog
// Evento: "Page.javascriptDialogOpening"
// Params: {
//   "url": "https://...",
//   "message": "Mensaje del dialog",
//   "type": "alert|confirm|prompt",
//   "defaultPrompt": "texto default" // solo en prompt
// }

// 3. Responder al dialog
{"method": "Page.handleJavaScriptDialog", "params": {
    "accept": true,  // true = OK, false = Cancel
    "promptText": "texto de respuesta" // opcional, solo para prompt
}}

Implementación interna

type dialogHandler struct {
    action     DialogAction
    promptText string
    callback   func(*Dialog) (DialogAction, string)
    done       chan struct{}
}

func (b *Browser) setupDialogHandling() {
    b.cdpClient.On("Page.javascriptDialogOpening", func(params json.RawMessage) {
        var event struct {
            Type          string `json:"type"`
            Message       string `json:"message"`
            DefaultPrompt string `json:"defaultPrompt"`
        }

        json.Unmarshal(params, &event)

        dialog := &Dialog{
            Type:              DialogType(event.Type),
            Message:           event.Message,
            DefaultPromptText: event.DefaultPrompt,
        }

        // Procesar con handler registrado
        action, text := b.processDialog(dialog)

        // Responder
        b.cdpClient.SendCommand(context.Background(), "Page.handleJavaScriptDialog", map[string]interface{}{
            "accept":     action == DialogAccept,
            "promptText": text,
        })
    })
}

Consideraciones especiales

Timing crítico

  • Los dialogs bloquean JavaScript hasta que se responden
  • Debe haber handler registrado ANTES de que aparezca el dialog
  • Si no se maneja, Chrome esperará indefinidamente

beforeunload dialogs

// Dialogs de "¿Seguro que quieres salir?"
// Se generan al cerrar tab/navegador
b.OnDialog(func(dialog *Dialog) (browser.DialogAction, string) {
    if dialog.Type == browser.DialogTypeBeforeUnload {
        return browser.DialogAccept, "" // Permitir salir
    }
    return browser.DialogAccept, ""
})

Headless mode

  • En modo headless, los dialogs no se muestran visualmente
  • Pero igual generan el evento y deben manejarse
  • Importante para testing automatizado

Timeout en dialogs

// Implementar timeout para evitar quedar colgado
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()

dialog, err := b.WaitForDialog(ctx)
if err == context.DeadlineExceeded {
    log.Println("No apareció dialog en 5s")
}

Testing

Página de prueba

<!DOCTYPE html>
<html>
<body>
    <button onclick="alert('Hola')">Alert</button>
    <button onclick="confirm('¿Continuar?')">Confirm</button>
    <button onclick="prompt('Nombre:')">Prompt</button>

    <script>
        // Test beforeunload
        window.addEventListener('beforeunload', (e) => {
            e.preventDefault();
            e.returnValue = '';
        });
    </script>
</body>
</html>

Tests

func TestAlertHandling(t *testing.T) {
    b.AutoHandleDialogs(ctx, browser.DialogAccept)
    b.Navigate(ctx, "test.html", nil)
    b.Click(ctx, "button:nth-child(1)")
    // No debe quedar colgado
}

func TestPromptResponse(t *testing.T) {
    b.OnDialog(func(d *browser.Dialog) (browser.DialogAction, string) {
        if d.Type == browser.DialogTypePrompt {
            return browser.DialogAccept, "Test Name"
        }
        return browser.DialogAccept, ""
    })

    b.Click(ctx, "button:nth-child(3)")
    result, _ := b.Evaluate(ctx, "lastPromptResult")
    assert.Equal(t, "Test Name", result.Value)
}

Ejemplos de uso real

Login con confirm

b.OnDialog(func(d *browser.Dialog) (browser.DialogAction, string) {
    if strings.Contains(d.Message, "logout") {
        return browser.DialogAccept, ""
    }
    return browser.DialogDismiss, ""
})

b.Click(ctx, "#logout-button")

Formulario con prompt

b.PromptDialog(ctx, "usuario@example.com")
b.Click(ctx, "#ask-email-button")

Referencias