c165f2f788
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/
7.8 KiB
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
- CDP Page.handleJavaScriptDialog: https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-handleJavaScriptDialog
- CDP Page.javascriptDialogOpening: https://chromedevtools.github.io/devtools-protocol/tot/Page/#event-javascriptDialogOpening
- Playwright Dialogs: https://playwright.dev/docs/dialogs
- Selenium Alerts: https://www.selenium.dev/documentation/webdriver/interactions/alerts/