4187f9b6b1
Tras estudiar el código de Playwright (sources/playwright), 4 primitivas nuevas y
1 endurecida para que la interacción web sea fiable:
- cdp_wait_actionable: visible + stable (2 rAF) + enabled + hit-test (elementFromPoint
cruzando shadow DOM) + retry backoff + scroll cycling. Devuelve el punto validado.
Réplica de _retryAction/_checkElementIsStable/expectHitTarget de Playwright.
- cdp_select_dropdown: desplegables custom (combobox/MUI/select2/headlessui): click real
en trigger -> espera apertura (aria-expanded/[role=option] visible) -> click real en
la opción. Resuelve el fallo nº1: clicar antes de que monte el listbox.
- cdp_select_option (endurecida v1.1.0): valida <select> real, match value/label
normalizado/índice, option.selected para multiple, eventos input{composed}+change.
- cdp_fill: escribir fiable en inputs React/Vue: focus -> select-all -> Input.insertText
(sin native value setter, como Playwright); native setter solo para inputs especiales.
- cdp_find_by_role: localizar por rol ARIA + accessible name (estilo getByRole),
reutilizando el AX tree de cdp_get_ax_outline.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
108 lines
5.7 KiB
Markdown
108 lines
5.7 KiB
Markdown
---
|
|
name: cdp_select_option
|
|
kind: function
|
|
lang: go
|
|
domain: browser
|
|
version: "1.1.0"
|
|
purity: impure
|
|
signature: "func CdpSelectOption(c *CDPConn, selector string, value string) error"
|
|
description: "Selecciona una <option> de un <select> nativo (localizado por selector CSS) replicando la semantica de Playwright (injectedScript.selectOptions). Match por value exacto, luego label/texto exacto, luego label normalizado (whitespace-collapse + strip zero-width/soft-hyphen), luego substring normalizado, y por ultimo indice si value es entero. Setea option.selected (soporta <select multiple>), hace focus, y despacha 'input' {bubbles,composed} + 'change' {bubbles}. Valida que el elemento sea <select> (error claro si no) y sigue <label for>. Via Runtime.evaluate, reusa CdpEvaluate."
|
|
tags: [chrome, cdp, browser, automation, select, dropdown, form, dom, devtools]
|
|
uses_functions: [cdp_evaluate_go_browser]
|
|
uses_types: []
|
|
returns: []
|
|
returns_optional: false
|
|
error_type: "error_go_core"
|
|
imports: [fmt, strings]
|
|
params:
|
|
- name: c
|
|
desc: "conexión CDP activa"
|
|
- name: selector
|
|
desc: "selector CSS del elemento <select> a modificar"
|
|
- name: value
|
|
desc: "criterio de seleccion. Se prueba en orden: value exacto → label/texto exacto → label normalizado (whitespace-collapse + strip U+200B/U+00AD) → label por substring normalizado → indice (si value es un entero)"
|
|
output: "error si el selector no encuentra elemento (\"element not found\"), si el elemento no es un <select> (\"element is not a <select> ...\"), o si ninguna option coincide (\"option not found in <select>\"); nil si la selección y los eventos se despacharon correctamente"
|
|
tested: false
|
|
tests: []
|
|
test_file_path: ""
|
|
file_path: "functions/browser/cdp_select_option.go"
|
|
---
|
|
|
|
## Ejemplo
|
|
|
|
```go
|
|
conn, _ := CdpConnect(9222)
|
|
CdpNavigate(conn, "https://example.com/form")
|
|
|
|
// Seleccionar por value
|
|
if err := CdpSelectOption(conn, "#country", "ES"); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Seleccionar por texto visible cuando no se conoce el value interno
|
|
if err := CdpSelectOption(conn, "select[name=lang]", "Español"); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Seleccionar por indice (3a opcion) cuando ni value ni texto son estables
|
|
if err := CdpSelectOption(conn, "#size", "2"); err != nil { // index 2 = 3a option
|
|
log.Fatal(err)
|
|
}
|
|
```
|
|
|
|
## Cuando usarla
|
|
|
|
Usala cuando necesites elegir una opcion de un `<select>` nativo en un formulario
|
|
web y quieras que un framework (React, Vue, Angular) reaccione al cambio. Es la
|
|
forma robusta de rellenar dropdowns durante automatizacion/scraping: a diferencia
|
|
de un click sobre la option, setea `option.selected` y dispara `input`+`change`,
|
|
que es lo que los frameworks escuchan. Combinala con `CdpClick` para enviar el
|
|
formulario despues. Si no conoces el `value` interno, pasa el texto visible (se
|
|
normaliza el whitespace) o el indice numerico de la option.
|
|
|
|
## Gotchas
|
|
|
|
- **Solo `<select>` nativos.** Si el elemento no es un `<select>` retorna error
|
|
claro `element is not a <select> ...`. Dropdowns custom hechos con `<div>` + JS
|
|
(react-select, headlessui, Radix, etc.) NO son `<select>` reales: para esos usa
|
|
`cdp_select_dropdown` (cuando exista) o clica el trigger con `CdpClickRef` y
|
|
luego la opcion del menu desplegado (`CdpFindRefByText` + `CdpClickRef`). NO uses
|
|
esta funcion para ellos.
|
|
- **Orden de matching del `value` recibido** (se prueba en este orden y para en el
|
|
primer match):
|
|
1. `option.value` exacto (`===`).
|
|
2. `option.label` / `textContent` exacto (sin normalizar).
|
|
3. label/texto NORMALIZADO exacto: se quita zero-width space (U+200B) y soft
|
|
hyphen (U+00AD), se hace `trim`, y se colapsa cualquier whitespace (`\s+`) a un
|
|
solo espacio — igual que `normalizeWhiteSpace` de Playwright.
|
|
4. label/texto por SUBSTRING normalizado (primera option cuyo label normalizado
|
|
contenga el value normalizado). Util para etiquetas largas; cuidado con
|
|
ambiguedad (gana la primera en orden de documento).
|
|
5. fallback por INDICE: solo si `value` es un entero `>= 0` valido (`"2"` → 3a
|
|
option). Por eso un `value` que casualmente sea numerico puede caer aqui si no
|
|
hubo ningun match textual antes — preferi el `value` real cuando exista.
|
|
El matching es case-sensitive en todos los pasos (no se hace lowercase).
|
|
- **`<select multiple>` soportado:** setea `option.selected = true` sobre la option
|
|
encontrada sin tocar el resto de selecciones. En un `<select>` simple deselecciona
|
|
las demas antes de marcar la elegida. (La version 1.0.0 solo seteaba `select.value`
|
|
y reseteaba el multiple — corregido.)
|
|
- **Eventos:** dispara `input` con `{bubbles:true, composed:true}` (el `composed`
|
|
permite cruzar shadow DOM, p.ej. web components que envuelven el `<select>`) y
|
|
luego `change` con `{bubbles:true}`, en ese orden. Hace `focus()` del select antes.
|
|
- No hace scroll ni verifica visibilidad/enabled: opera sobre el DOM directamente.
|
|
Si el `<select>` o la `<option>` estan `disabled`, la seleccion se aplica igual
|
|
pero la UI puede ignorarla segun el framework (Playwright aqui devolveria
|
|
`optionnotenabled`; esta funcion no chequea enabled — mantiene KISS).
|
|
- Si el elemento aun no existe (carga dinamica), retorna `element not found` sin
|
|
esperar — combinar con `CdpWaitElement` para elementos diferidos.
|
|
|
|
## Capability growth log
|
|
|
|
- v1.1.0 (2026-06-16) — alineada con Playwright `injectedScript.selectOptions`:
|
|
valida que el elemento sea `<select>` (error claro si no, apuntando a dropdowns
|
|
custom), sigue `<label for>`, matching multi-criterio (value → label exacto →
|
|
label normalizado whitespace-collapse → substring → indice), usa
|
|
`option.selected` en vez de solo `select.value` (soporta `<select multiple>`),
|
|
añade `composed:true` al evento `input` (cruza shadow DOM) y `focus()` previo.
|
|
Firma intacta (no rompe el caller del MCP `dom_select_option`).
|