defmodule MvWeb.MemberLive.IndexGroupsAccessibilityTest do @moduledoc """ Tests for accessibility of groups feature in the member overview. Tests cover: - Badges have aria-label for group membership (no role="status"; reserved for live regions) - 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 aria-label for screen readers", %{ conn: conn, member1: member1, group1: group1 } do conn = conn_with_oidc_user(conn) {:ok, view, html} = live(conn, "/members") # Verify badges have aria-label containing the group name (no role=status on badges) assert has_element?(view, "span[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 group presence section with legend", %{ conn: conn } do conn = conn_with_oidc_user(conn) {:ok, view, _html} = live(conn, "/members") # Open filter dropdown view |> element("button[aria-label='Filter members']") |> render_click() html = render(view) # Groups section: legend "Member has groups" and radios (Any / Yes / No) assert html =~ ~r/[Gg]roups/ assert has_element?(view, "[data-testid='member-filter-form']") 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 describing the sort state assert has_element?(view, "[data-testid='groups'][aria-label]") end @tag :ui test "keyboard navigation works for filter dropdown", %{ conn: conn, member1: member1, group1: group1 } do conn = conn_with_oidc_user(conn) {:ok, view, _html} = live(conn, "/members") view |> element("button[aria-label='Filter members']") |> render_click() view |> element("[data-testid='member-filter-form']") |> render_change(%{"group_#{group1.id}" => "in", "payment_filter" => "all"}) html = render(view) assert html =~ member1.first_name 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") assert has_element?(view, "[data-testid='groups']") view |> element("[data-testid='groups']") |> render_click() # Verify sort was applied (URL may include other params) assert has_element?(view, "[data-testid='groups'][aria-label*='ascending']") 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") view |> element("button[aria-label='Filter members']") |> render_click() view |> element("[data-testid='member-filter-form']") |> render_change(%{"group_#{group1.id}" => "in", "payment_filter" => "all"}) html = render(view) assert html =~ member1.first_name 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