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