# 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 ```go // 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 ```go // 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 ```go 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 ```go // 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 ```go // Preparar handler antes de la acción b.AcceptDialog(ctx) // Acción que genera dialog b.Click(ctx, "#show-alert") ``` ## Comandos CDP necesarios ```go // 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 ```go 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 ```go // 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 ```go // 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 ```html ``` ### Tests ```go 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 ```go 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 ```go 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/