defmodule MvWeb.GroupLive.ShowAccessibilityTest do @moduledoc """ Accessibility tests for Add/Remove Member functionality. Tests ARIA labels, keyboard navigation, and screen reader support. """ use MvWeb.ConnCase, async: false import Phoenix.LiveViewTest use Gettext, backend: MvWeb.Gettext alias Mv.Membership alias Mv.Fixtures describe "ARIA labels and roles" do test "modal has role='dialog' with aria-labelledby and aria-describedby", %{conn: conn} do group = Fixtures.group_fixture() {:ok, view, _html} = live(conn, "/groups/#{group.slug}") # Open modal view |> element("button", "Add Member") |> render_click() html = render(view) # Modal should have proper ARIA attributes assert html =~ ~r/role=["']dialog["']/ || html =~ ~r/aria-labelledby/ || html =~ ~r/aria-describedby/ end test "search input has correct aria-label and aria-autocomplete attributes", %{conn: conn} do group = Fixtures.group_fixture() {:ok, view, _html} = live(conn, "/groups/#{group.slug}") # Open modal view |> element("button", "Add Member") |> render_click() html = render(view) # Search input should have ARIA attributes assert html =~ ~r/aria-label.*[Ss]earch.*member/ || html =~ ~r/aria-autocomplete=["']list["']/ end test "remove button has aria-label with tooltip text", %{conn: conn} do system_actor = Mv.Helpers.SystemActor.get_system_actor() group = Fixtures.group_fixture() {:ok, member} = Membership.create_member( %{ first_name: "Alice", last_name: "Smith", email: "alice@example.com" }, actor: system_actor ) Membership.create_member_group(%{member_id: member.id, group_id: group.id}, actor: system_actor ) {:ok, view, _html} = live(conn, "/groups/#{group.slug}") html = render(view) # Remove button should have aria-label assert html =~ ~r/aria-label.*[Rr]emove/ || html =~ ~r/aria-label.*member/i end test "add button has correct aria-label", %{conn: conn} do group = Fixtures.group_fixture() {:ok, view, _html} = live(conn, "/groups/#{group.slug}") # Open modal view |> element("button", "Add Member") |> render_click() html = render(view) # Add button should have aria-label assert html =~ ~r/aria-label.*[Aa]dd/ || html =~ ~r/button.*[Aa]dd/ end end describe "keyboard navigation" do test "tab navigation works in modal", %{conn: conn} do # This test verifies that keyboard navigation is possible # Actual tab order testing would require more complex setup group = Fixtures.group_fixture() {:ok, view, _html} = live(conn, "/groups/#{group.slug}") # Open modal view |> element("button", "Add Member") |> render_click() html = render(view) # Modal should have focusable elements assert html =~ ~r/input|button/ || html =~ "#member-search-input" end test "escape key closes modal", %{conn: conn} do group = Fixtures.group_fixture() {:ok, view, _html} = live(conn, "/groups/#{group.slug}") # Open modal view |> element("button", "Add Member") |> render_click() assert has_element?(view, "#add-member-modal") || has_element?(view, "[role='dialog']") # Send escape key event (if implemented) # Note: Implementation should handle phx-window-keydown="escape" or similar # For now, we verify modal can be closed via Cancel button view |> element("button", "Cancel") |> render_click() refute has_element?(view, "#add-member-modal") end test "enter/space activates buttons when focused", %{conn: conn} do # This test verifies that buttons can be activated via keyboard # Actual keyboard event testing would require more complex setup system_actor = Mv.Helpers.SystemActor.get_system_actor() group = Fixtures.group_fixture() {:ok, member} = Membership.create_member( %{ first_name: "Bob", last_name: "Jones", email: "bob@example.com" }, actor: system_actor ) {:ok, view, _html} = live(conn, "/groups/#{group.slug}") # Open modal view |> element("button", "Add Member") |> render_click() # Select member view |> element("#member-search-input") |> render_focus() view |> element("#member-search-input") |> render_change(%{"member_search" => "Bob"}) view |> element("[data-member-id='#{member.id}']") |> render_click() # Add button should be enabled and clickable view |> element("button", "Add") |> render_click() # Should succeed html = render(view) assert html =~ "Bob" || html =~ gettext("Member added successfully") end test "focus management: focus is set to modal when opened", %{conn: conn} do # This test verifies that focus is properly managed # When modal opens, focus should move to modal (first focusable element) group = Fixtures.group_fixture() {:ok, view, _html} = live(conn, "/groups/#{group.slug}") # Open modal view |> element("button", "Add Member") |> render_click() html = render(view) # Modal should be visible and focusable assert html =~ "#member-search-input" || html =~ ~r/autofocus|tabindex/ end end describe "screen reader support" do test "modal title is properly associated", %{conn: conn} do group = Fixtures.group_fixture() {:ok, view, _html} = live(conn, "/groups/#{group.slug}") # Open modal view |> element("button", "Add Member") |> render_click() html = render(view) # Modal should have title assert html =~ gettext("Add Member to Group") || html =~ ~r/ element("button", "Add Member") |> render_click() # Search view |> element("#member-search-input") |> render_focus() view |> element("#member-search-input") |> render_change(%{"member_search" => "Charlie"}) html = render(view) # Search results should have proper ARIA attributes assert html =~ ~r/role=["']listbox["']/ || html =~ ~r/role=["']option["']/ || html =~ "Charlie" end test "flash messages are properly announced", %{conn: conn} do system_actor = Mv.Helpers.SystemActor.get_system_actor() group = Fixtures.group_fixture() {:ok, member} = Membership.create_member( %{ first_name: "David", last_name: "Wilson", email: "david@example.com" }, actor: system_actor ) {:ok, view, _html} = live(conn, "/groups/#{group.slug}") # Add member view |> element("button", "Add Member") |> render_click() view |> element("#member-search-input") |> render_focus() view |> element("#member-search-input") |> render_change(%{"member_search" => "David"}) view |> element("[data-member-id='#{member.id}']") |> render_click() view |> element("button", "Add") |> render_click() html = render(view) # Flash message should have proper ARIA attributes for screen readers assert html =~ gettext("Member added successfully") || html =~ ~r/role=["']status["']/ || html =~ ~r/aria-live/ end end end