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 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 # Sort header should have aria-label describing the sort state assert html =~ ~r/aria-label=.*[Gg]roup/ or 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