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>
5.2 KiB
5.2 KiB
name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, uses_types, returns, returns_optional, error_type, imports, tested, tests, test_file_path, params, output, file_path
| name | kind | lang | domain | version | purity | signature | description | tags | uses_functions | uses_types | returns | returns_optional | error_type | imports | tested | tests | test_file_path | params | output | file_path | |||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| cdp_wait_actionable | function | go | browser | 1.0.0 | impure | func CdpWaitActionable(c *CDPConn, backendNodeID int, needEnabled bool, timeout time.Duration) (x float64, y float64, err error) | Bloquea hasta que el elemento del #ref sea accionable (listo para un click/hover fiable) o expire timeout. Reproduce el modelo de actionability de Playwright: en bucle con backoff [0,20,100,100,500]ms comprueba visible (client rects + computed style), stable (mismo getBoundingClientRect en dos requestAnimationFrame seguidos), enabled opcional (disabled / aria-disabled / fieldset disabled subiendo la jerarquía), scroll into view rotando alineación block (center/start/end), y hit-test (elementFromPoint subiendo por shadow DOM apunta al target o descendiente). Devuelve el punto central (x,y) en coords de viewport listo para Input.dispatchMouseEvent. Al expirar, el error indica qué estado falló (not visible / not stable / disabled / outside viewport / intercepted by other element). |
|
false | error_go_core | false |
|
(x, y) punto central del elemento en coordenadas de viewport (CSS px), listo para despachar el pointer, cuando todos los chequeos pasan; error si la conexión es nil, el nodo no resuelve a objectId, se desconecta del DOM, o expira el timeout (con el estado que falló al final). | functions/browser/cdp_wait_actionable.go |
Ejemplo
// Tras un page_perceive que devuelve outline con #ref=1234, esperar a que el
// elemento sea accionable y luego clicar el punto exacto que devuelve:
conn, _ := CdpConnect(9222)
x, y, err := CdpWaitActionable(conn, 1234, true, 5*time.Second)
if err != nil {
log.Fatalf("no accionable: %v", err) // ej: "intercepted by other element: div#cookie-banner"
}
// x,y ya están en viewport, estables y sin overlay encima: click fiable.
_ = CdpClickXYHuman(conn, x, y, MouseHumanOpts{})
Cuando usarla
Antes de CUALQUIER click/hover/type que deba ser fiable sobre un #ref del outline.
Llamarla justo después de page_perceive y antes de cdp_click_ref /
cdp_click_xy_human / dom_*_ref para evitar los fallos clásicos del navegador:
clicar un botón que aún se está animando hacia su posición, un elemento tapado por
un banner de cookies / modal / spinner, o un control todavía disabled. Es la
puerta de actionability que separa "el nodo existe en el DOM" de "el nodo está
listo para recibir el evento ahí donde lo voy a despachar". Usar needEnabled=true
para botones/inputs/enlaces; needEnabled=false para hover sobre texto o medir un
contenedor.
Gotchas
- Coste de polling. Es síncrona y bloqueante: hace un
Runtime.callFunctionOnpor iteración + 2requestAnimationFramepor chequeo de estabilidad. En el peor caso poll-ea hastatimeoutcon backoff creciente (0,20,100,100,500ms → 500ms). No la metas en un bucle apretado sobre N elementos sin necesidad; una sola llamada por acción es lo correcto. Timeouts altos sobre elementos que nunca llegan (genuinamente ocultos) cuestan el timeout entero. - Shadow DOM. El hit-test sube por shadow roots (
assignedSlot/parentNode.host) y por eso funciona con web components con shadow root abierto. Con shadow roots cerradoselementFromPointno expone el interior y el hit-test puede reportarinterceptederróneamente; en ese caso usar el click víaelement.click()(modo instant decdp_click_ref), que no depende del hit-test geométrico. - iframes. Opera sobre el contexto de la página/frame al que apunta el
*CDPConn. UnbackendNodeIDde otro frame no resuelve aquí: hay que tener la conexión/contexto del frame correcto (vercdp_eval_in_frame). Las coordenadas devueltas son relativas al viewport de ESE documento, no compuestas con el offset del iframe en la página padre. - Estabilidad vs animaciones infinitas. Un elemento con una animación CSS
perpetua que mueve su rect (spinner que se desplaza, marquee) nunca pasará el
chequeo
stabley agotará el timeout con "not stable". Es comportamiento correcto (no es accionable de forma fiable), pero conviene saberlo. - El punto devuelto es (x,y) de viewport, no de página. Es lo que
Input.dispatchMouseEventespera. Si necesitas coords de página (con scroll), el JS interno ya las calcula (pageX/pageY) pero la firma pública expone solo las de viewport para encajar con el dispatch de pointer.