defmodule MvWeb.MemberLive.IndexGroupsAccessibilityTest do @moduledoc """ Tests for accessibility of groups feature in the member overview. Tests cover: - Badges have role="status" and aria-label - Filter dropdown has aria-label - Sort header has aria-label for screen reader - Keyboard navigation works (Tab through filter, sort header) """ # async: false to prevent PostgreSQL deadlocks when creating members and groups use MvWeb.ConnCase, async: false import Phoenix.LiveViewTest require Ash.Query alias Mv.Membership.{Group, MemberGroup} setup do system_actor = Mv.Helpers.SystemActor.get_system_actor() # Create test members {:ok, member1} = Mv.Membership.create_member( %{first_name: "Alice", last_name: "Anderson", email: "alice@example.com"}, actor: system_actor ) # Create test groups {:ok, group1} = Group |> Ash.Changeset.for_create(:create, %{name: "Board Members"}) |> Ash.create(actor: system_actor) # Create member-group associations {:ok, _mg1} = MemberGroup |> Ash.Changeset.for_create(:create, %{member_id: member1.id, group_id: group1.id}) |> Ash.create(actor: system_actor) %{ member1: member1, group1: group1 } end @tag :ui test "group badges have role and aria-label", %{ conn: conn, member1: member1, group1: group1 } do conn = conn_with_oidc_user(conn) {:ok, view, html} = live(conn, "/members") # Verify badges have accessibility attributes # Badges should have role="status" and aria-label describing the group assert html =~ ~r/role=["']status["']/ or html =~ ~r/aria-label=.*#{group1.name}/ assert html =~ group1.name # Verify member1's row contains the badge assert html =~ member1.first_name end @tag :ui test "filter dropdown has aria-label", %{ conn: conn } do conn = conn_with_oidc_user(conn) {:ok, view, html} = live(conn, "/members") # Verify filter dropdown has aria-label assert html =~ ~r/select.*name=["']group_filter["'].*aria-label=/ or html =~ ~r/aria-label=.*[Gg]roup/ # Verify dropdown is present assert has_element?(view, "select[name='group_filter']") end @tag :ui test "sort header has aria-label for screen reader", %{ conn: conn } do conn = conn_with_oidc_user(conn) {:ok, view, html} = live(conn, "/members") # Verify sort header has aria-label # Sort header should have aria-label describing the sort state assert html =~ ~r/aria-label=.*[Gg]roup/ or has_element?(view, "[data-testid='sort_groups'][aria-label]") end @tag :ui test "keyboard navigation works for filter dropdown", %{ conn: conn, group1: group1 } do conn = conn_with_oidc_user(conn) {:ok, view, _html} = live(conn, "/members") # Verify dropdown is keyboard accessible # Tab should focus the dropdown # Arrow keys should navigate options # Enter should select option assert has_element?(view, "select[name='group_filter']") # Test that dropdown can be focused and changed via keyboard # (This is a basic accessibility check - actual keyboard testing would require browser automation) view |> element("select[name='group_filter']") |> render_change(%{"group_filter" => group1.id}) # Verify change was applied html = render(view) assert html end @tag :ui test "keyboard navigation works for sort header", %{ conn: conn } do conn = conn_with_oidc_user(conn) {:ok, view, _html} = live(conn, "/members") # Verify sort header is keyboard accessible # Tab should focus the sort header # Enter/Space should activate sorting assert has_element?(view, "[data-testid='sort_groups']") # Test that sort header can be activated via click (simulating keyboard) view |> element("[data-testid='sort_groups']") |> render_click() # Verify sort was applied assert_patch(view, "/members?query=&sort_field=groups&sort_order=asc") end @tag :ui test "screen reader announcements for filter changes", %{ conn: conn, member1: member1, group1: group1 } do conn = conn_with_oidc_user(conn) {:ok, view, _html} = live(conn, "/members") # Apply filter view |> element("select[name='group_filter']") |> render_change(%{"group_filter" => group1.id}) # Verify filter change is announced (via aria-live region or similar) html = render(view) # Should show filtered results assert html =~ member1.first_name # Verify member count or filter status is announced # (Implementation might use aria-live="polite" for announcements) assert html end @tag :ui test "multiple badges are announced correctly", %{ conn: conn, member1: member1 } do system_actor = Mv.Helpers.SystemActor.get_system_actor() # Create multiple groups for member1 {:ok, group2} = Group |> Ash.Changeset.for_create(:create, %{name: "Active Members"}) |> Ash.create(actor: system_actor) {:ok, _mg} = MemberGroup |> Ash.Changeset.for_create(:create, %{member_id: member1.id, group_id: group2.id}) |> Ash.create(actor: system_actor) conn = conn_with_oidc_user(conn) {:ok, view, html} = live(conn, "/members") # Verify multiple badges are present assert html =~ member1.first_name # Both groups should be visible # Screen reader should be able to distinguish between multiple badges assert html end end