defmodule MvWeb.MemberLive.ShowGroupsDisplayTest do @moduledoc """ Tests for displaying groups in the member detail view (Issue #374). Tests cover: - Groups in Personal Data (with and without groups) - Group links with correct names and links to group detail pages - Edge cases (one group, many groups) - Security: groups visible only when user may view member - Accessibility: group links have aria-label for screen readers ## Note on async async: false to avoid PostgreSQL deadlocks when creating members and groups in the same test run (same as IndexGroupsDisplayTest). """ use MvWeb.ConnCase, async: false import Phoenix.LiveViewTest require Ash.Query use Gettext, backend: MvWeb.Gettext alias Mv.Membership.{Group, MemberGroup} describe "groups section" do setup do actor = Mv.Helpers.SystemActor.get_system_actor() {:ok, member} = create_member(actor, %{ first_name: "Alice", last_name: "Anderson", email: "alice@example.com" }) %{member: member, actor: actor} end test "displays Groups section when member has at least one group", %{ conn: conn, member: member, actor: actor } do {:ok, group} = create_group(actor, "Board Members") {:ok, _mg} = add_member_to_group(member, group, actor) conn = conn_with_oidc_user(conn) {:ok, _view, html} = live(conn, ~p"/members/#{member}") assert html =~ gettext("Groups") assert html =~ group.name end test "displays all group links with correct names when member is in multiple groups", %{ conn: conn, member: member, actor: actor } do {:ok, group1} = create_group(actor, "Board Members") {:ok, group2} = create_group(actor, "Active Members") {:ok, _mg1} = add_member_to_group(member, group1, actor) {:ok, _mg2} = add_member_to_group(member, group2, actor) conn = conn_with_oidc_user(conn) {:ok, _view, html} = live(conn, ~p"/members/#{member}") assert html =~ gettext("Groups") assert html =~ group1.name assert html =~ group2.name end test "displays Groups section when member has no groups (empty state)", %{ conn: conn, member: member } do conn = conn_with_oidc_user(conn) {:ok, _view, html} = live(conn, ~p"/members/#{member}") # Groups are in Personal Data; label "Groups" and empty state "No groups" must be present assert html =~ gettext("Groups") assert html =~ gettext("No groups") end test "renders all group names when member has multiple groups", %{ conn: conn, member: member, actor: actor } do {:ok, group1} = create_group(actor, "Alpha") {:ok, group2} = create_group(actor, "Beta") {:ok, _mg1} = add_member_to_group(member, group1, actor) {:ok, _mg2} = add_member_to_group(member, group2, actor) conn = conn_with_oidc_user(conn) {:ok, _view, html} = live(conn, ~p"/members/#{member}") assert html =~ "Alpha" assert html =~ "Beta" end end describe "groups section links" do setup do actor = Mv.Helpers.SystemActor.get_system_actor() {:ok, member} = create_member(actor, %{first_name: "Bob", last_name: "Brown", email: "bob@example.com"}) {:ok, group} = create_group(actor, "Board Members") {:ok, _mg} = add_member_to_group(member, group, actor) %{member: member, group: group} end test "each group link goes to group detail page with correct slug", %{ conn: conn, member: member, group: group } do conn = conn_with_oidc_user(conn) {:ok, view, _html} = live(conn, ~p"/members/#{member}") # Link to group detail: /groups/:slug (slug is URL-friendly, e.g. "board-members") assert has_element?(view, "a[href*='/groups/#{group.slug}']", group.name) end test "clicking group link navigates to group detail page", %{ conn: conn, member: member, group: group } do conn = conn_with_oidc_user(conn) {:ok, view, _html} = live(conn, ~p"/members/#{member}") view |> element("a[href*='/groups/#{group.slug}']") |> render_click() assert_redirect(view, ~p"/groups/#{group.slug}") end end describe "groups section edge cases" do setup do actor = Mv.Helpers.SystemActor.get_system_actor() {:ok, member} = create_member(actor, %{ first_name: "Charlie", last_name: "Clark", email: "charlie@example.com" }) %{member: member, actor: actor} end test "member in exactly one group shows single link", %{ conn: conn, member: member, actor: actor } do {:ok, group} = create_group(actor, "Solo Group") {:ok, _mg} = add_member_to_group(member, group, actor) conn = conn_with_oidc_user(conn) {:ok, _view, html} = live(conn, ~p"/members/#{member}") assert html =~ gettext("Groups") assert html =~ group.name end test "member in many groups shows all group links", %{ conn: conn, member: member, actor: actor } do group_names = Enum.map(1..5, fn i -> "Group #{i}" end) groups = Enum.map(group_names, fn name -> {:ok, g} = create_group(actor, name) g end) for g <- groups do {:ok, _mg} = add_member_to_group(member, g, actor) end conn = conn_with_oidc_user(conn) {:ok, _view, html} = live(conn, ~p"/members/#{member}") assert html =~ gettext("Groups") for name <- group_names do assert html =~ name end end end describe "groups section with read_only user" do @tag role: :read_only test "user with read permission sees Groups section", %{conn: conn} do actor = Mv.Helpers.SystemActor.get_system_actor() {:ok, member} = create_member(actor, %{ first_name: "Diana", last_name: "Davis", email: "diana@example.com" }) {:ok, group} = create_group(actor, "Readers") {:ok, _mg} = add_member_to_group(member, group, actor) {:ok, _view, html} = live(conn, ~p"/members/#{member}") assert html =~ gettext("Groups") assert html =~ group.name end end describe "groups section accessibility" do setup do actor = Mv.Helpers.SystemActor.get_system_actor() {:ok, member} = create_member(actor, %{first_name: "Eve", last_name: "Evans", email: "eve@example.com"}) {:ok, group} = create_group(actor, "A11y Group") {:ok, _mg} = add_member_to_group(member, group, actor) %{member: member, group: group} end test "group links have aria-label for screen readers", %{ conn: conn, member: member, group: group } do conn = conn_with_oidc_user(conn) {:ok, view, html} = live(conn, ~p"/members/#{member}") assert html =~ group.name # Group link has aria-label indicating group membership for screen readers assert has_element?(view, "a[aria-label*='#{group.name}']") end end # Helpers to reduce setup duplication (create member/group, assign member to group). defp create_member(actor, attrs) do Mv.Membership.create_member(attrs, actor: actor) end defp create_group(actor, name) do Group |> Ash.Changeset.for_create(:create, %{name: name}) |> Ash.create(actor: actor) end defp add_member_to_group(member, group, actor) do MemberGroup |> Ash.Changeset.for_create(:create, %{member_id: member.id, group_id: group.id}) |> Ash.create(actor: actor) end end