mitgliederverwaltung/test/mv_web/member_live/index_groups_accessibility_test.exs
Simon c547ca19af feat(overview): replace filter panel with add-filter builder and aging column
Swaps the unbounded vertical filter panel for a Polaris-style add-filter
builder (searchable grouped field picker, type-aware value control, applied
chips) and colour-codes the fees column with each member's period-scoped
unpaid-cycle count.
2026-07-10 16:27:15 +02:00

171 lines
4.9 KiB
Elixir

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)
"""
# Kept async: false as a deferred scope decision. The deferrable-FK migration
# removed the concurrent-create_member deadlock that previously forced this, so
# re-flipping this members/groups suite to async is a possible follow-up rather
# than part of the original change.
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
alias Mv.Membership.{Group, MemberGroup}
require Ash.Query
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 "field picker exposes a combobox with a Group field option", %{
conn: conn
} do
conn = conn_with_oidc_user(conn)
{:ok, view, _html} = live(conn, "/members")
# Open the add-filter picker (combobox + listbox of grouped fields)
open_picker(view)
assert has_element?(view, "[data-testid='field-combobox'][role='combobox']")
assert has_element?(view, "#member-filter-field-listbox[role='listbox']")
assert has_element?(view, "#member-filter-field-opt-group[role='option']")
# Picking the Group field opens its value control with per-group legends.
view |> element("#member-filter-field-opt-group") |> render_click()
assert has_element?(view, "[data-testid='value-control-group']")
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")
apply_group_filter(view, group1.id, "in")
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")
apply_group_filter(view, group1.id, "in")
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