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/
This commit is contained in:
@@ -0,0 +1,300 @@
|
||||
# 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
|
||||
<!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
|
||||
```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/
|
||||
Reference in New Issue
Block a user