@@ -217,6 +217,13 @@ defmodule PhoenixTest.Query do
217217 def find_by_label ( html , input_selectors , label , opts \\ [ exact: true ] ) do
218218 input_selectors = List . wrap ( input_selectors )
219219
220+ case find_by_label_element ( html , input_selectors , label , opts ) do
221+ { :found , _element } = found -> found
222+ not_found -> with :not_found <- find_by_aria ( html , input_selectors , label , opts ) , do: not_found
223+ end
224+ end
225+
226+ defp find_by_label_element ( html , input_selectors , label , opts ) do
220227 case find_labels ( html , input_selectors , label , opts ) do
221228 { :implicit_association , _label_element , element } ->
222229 { :found , element }
@@ -254,6 +261,62 @@ defmodule PhoenixTest.Query do
254261 end
255262 end
256263
264+ # Finds elements whose accessible name (via `aria-label` or `aria-labelledby`)
265+ # matches `label`. Referenced `aria-labelledby` ids are resolved document-wide.
266+ defp find_by_aria ( html , input_selectors , label , opts ) do
267+ parsed = Html . parse_fragment ( html )
268+
269+ input_selectors
270+ |> Enum . flat_map ( fn selector ->
271+ parsed
272+ |> Html . all ( selector )
273+ |> Enum . filter ( & aria_name_match? ( parsed , & 1 , label , opts ) )
274+ end )
275+ |> case do
276+ [ ] -> :not_found
277+ [ element ] -> { :found , element }
278+ [ _first | _rest ] = found -> { :not_found , :found_many_labels_with_inputs , [ ] , found }
279+ end
280+ end
281+
282+ defp aria_name_match? ( parsed , element , label , opts ) do
283+ aria_label_match? ( element , label , opts ) or aria_labelledby_match? ( parsed , element , label , opts )
284+ end
285+
286+ defp aria_label_match? ( element , label , opts ) do
287+ case Html . attribute ( element , "aria-label" ) do
288+ nil -> false
289+ value -> text_match? ( normalize_whitespace ( value ) , label , opts )
290+ end
291+ end
292+
293+ defp aria_labelledby_match? ( parsed , element , label , opts ) do
294+ case Html . attribute ( element , "aria-labelledby" ) do
295+ nil ->
296+ false
297+
298+ ids ->
299+ text =
300+ ids
301+ |> String . split ( )
302+ |> Enum . map_join ( " " , & labelledby_text ( parsed , & 1 ) )
303+ |> normalize_whitespace ( )
304+
305+ text != "" and text_match? ( text , label , opts )
306+ end
307+ end
308+
309+ defp labelledby_text ( parsed , id ) do
310+ case parsed |> Html . all ( "[id='#{ id } ']" ) |> Enum . at ( 0 ) do
311+ nil -> ""
312+ element -> Html . element_text ( element )
313+ end
314+ end
315+
316+ defp normalize_whitespace ( string ) do
317+ string |> String . replace ( ~r/ \s +/ , " " ) |> String . trim ( )
318+ end
319+
257320 defp find_labels ( html , input_selectors , label , opts ) do
258321 html
259322 |> find ( "label" , label , opts )
@@ -472,25 +535,18 @@ defmodule PhoenixTest.Query do
472535 end
473536
474537 defp filter_by_element_text ( elements , text , opts ) do
475- exact_match = Keyword . get ( opts , :exact , false )
476-
477- filter_fun =
478- if exact_match do
479- & ( Html . element_text ( & 1 ) == text )
480- else
481- & ( Html . element_text ( & 1 ) =~ text )
482- end
483-
484- Enum . filter ( elements , filter_fun )
538+ Enum . filter ( elements , & text_match? ( Html . element_text ( & 1 ) , text , opts ) )
485539 end
486540
487541 defp find_first_by_element_text ( elements , text , opts ) do
488- exact_match = Keyword . get ( opts , :exact , false )
542+ Enum . find ( elements , & text_match? ( Html . element_text ( & 1 ) , text , opts ) )
543+ end
489544
490- if exact_match do
491- Enum . find ( elements , & ( Html . element_text ( & 1 ) == text ) )
545+ defp text_match? ( subject , text , opts ) do
546+ if Keyword . get ( opts , :exact , false ) do
547+ subject == text
492548 else
493- Enum . find ( elements , & ( Html . element_text ( & 1 ) =~ text ) )
549+ subject =~ text
494550 end
495551 end
496552
0 commit comments