Files
fn_registry/functions/browser/cdp_select_option.md
T
Egutierrez 4187f9b6b1 feat(browser): actionability + dropdowns + fill + role locator (estilo Playwright)
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>
2026-06-16 20:49:37 +02:00

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`).