--- name: cdp_find_by_role kind: function lang: go domain: browser version: "1.0.0" purity: impure signature: "func CdpFindByRole(c *CDPConn, role string, opts CdpFindByRoleOpts) (ref int, count int, err error)" description: "Localiza el primer elemento por su ROLE ARIA + accessible name (estilo getByRole de Playwright) reusando el AX tree (Accessibility.getFullAXTree). Devuelve el backendDOMNodeId (#ref) del primer match y el total de matches para detectar ambiguedad." tags: [browser] params: - name: c desc: "Conexion CDP viva (*CDPConn) del pool. nil => error." - name: role desc: "Rol ARIA exacto a matchear (ej 'button', 'link', 'textbox', 'checkbox')." - name: opts desc: "CdpFindByRoleOpts: Name (accessible name, vacio = no filtra), Exact (igualdad en vez de substring), Regex (Name como expresion regular RE2), CaseSensitive (default false)." output: "(ref int, count int, err error): ref = backendDOMNodeId del primer match (#ref para CdpClickRef/CdpHoverRef); count = total de matches (>1 = ambiguo); err si conexion nula, role vacio, regex invalida, fallo CDP o 0 matches." uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "error_go_core" imports: [] tested: false tests: [] test_file_path: "" file_path: "functions/browser/cdp_find_by_role.go" --- ## Ejemplo ```go c, _ := browser.CdpConnect(9333) // conexion CDP del pool ref, count, err := browser.CdpFindByRole(c, "button", browser.CdpFindByRoleOpts{ Name: "Aceptar", // substring del accessible name, case-insensitive }) if err != nil { log.Fatal(err) // ej: no element with role "button" and name "Aceptar" } if count > 1 { log.Printf("aviso: %d botones matchean 'Aceptar', usando el primero", count) } // ref es el mismo #ref que produce page_perceive: alimentarlo a CdpClickRef. _ = browser.CdpClickRef(c, ref, browser.MouseHumanOpts{}) // Match exacto + case-sensitive: ref, _, _ = browser.CdpFindByRole(c, "link", browser.CdpFindByRoleOpts{ Name: "Iniciar sesion", Exact: true, CaseSensitive: true, }) // Match por regex (ej "Eliminar 3 elementos" / "Eliminar 12 elementos"): ref, _, _ = browser.CdpFindByRole(c, "button", browser.CdpFindByRoleOpts{ Name: `^Eliminar \d+ elementos$`, Regex: true, }) ``` ## Cuando usarla Cuando necesites localizar un control de forma robusta a cambios de DOM/CSS: el rol ARIA + accessible name sobreviven a refactors de markup y clases CSS que romperian un selector `nth-of-type`. Es el patron primario que recomienda Playwright (getByRole) para encontrar elementos accionables (botones, links, inputs). Combina el `ref` devuelto directamente con `cdp_click_ref` / `cdp_hover_ref` para actuar sin pasar por un selector fragil. Revisa `count` antes de actuar: si es >1 la busqueda es ambigua y conviene refinar (Name mas especifico, Exact, o Regex anclada). ## Gotchas - El `name` que se matchea es el **accessible name computado** por el motor de accesibilidad de Chrome (deriva de aria-label, label asociado, contenido, alt, title segun la spec ARIA), **no** el `innerText` del elemento. Si buscas por el texto visible literal, usa `cdp_find_ref_by_text` en su lugar. - `count > 1` => ambiguedad: se devuelve el primer match en orden del AX tree, que no siempre es el visualmente primero ni el que quieres. Refina la busqueda. - El `role` se compara por **igualdad exacta** del rol ARIA: "button" no matchea "menuitem" aunque ambos sean clicables. Mira el outline de `page_perceive` / `cdp_get_ax_outline` para ver el rol real que Chrome asigna a cada nodo. - Nodos `ignored` del AX tree se descartan. Si el elemento esta oculto (aria-hidden, display:none) puede no aparecer y dar 0 matches. - El `ref` es un `backendDOMNodeId`: estable mientras el nodo viva, pero si el DOM muta entre el find y el click el ref puede quedar obsoleto.