Merge branch 'main' into bug/289_rauthy_error_message
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
commit
2315f2588f
16 changed files with 1136 additions and 88 deletions
|
|
@ -8,6 +8,7 @@ defmodule Mv.Membership.CustomFieldValidationTest do
|
|||
- Description length validation (max 500 characters)
|
||||
- Description trimming
|
||||
- Required vs optional fields
|
||||
- Value type immutability (cannot be changed after creation)
|
||||
"""
|
||||
use Mv.DataCase, async: true
|
||||
|
||||
|
|
@ -207,4 +208,101 @@ defmodule Mv.Membership.CustomFieldValidationTest do
|
|||
assert [%{field: :value_type}] = changeset.errors
|
||||
end
|
||||
end
|
||||
|
||||
describe "value_type immutability" do
|
||||
test "rejects attempt to change value_type after creation", %{actor: actor} do
|
||||
# Create custom field with value_type :string
|
||||
{:ok, custom_field} =
|
||||
CustomField
|
||||
|> Ash.Changeset.for_create(:create, %{
|
||||
name: "test_field",
|
||||
value_type: :string
|
||||
})
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
original_value_type = custom_field.value_type
|
||||
assert original_value_type == :string
|
||||
|
||||
# Attempt to update value_type to :integer
|
||||
assert {:error, %Ash.Error.Invalid{} = error} =
|
||||
custom_field
|
||||
|> Ash.Changeset.for_update(:update, %{
|
||||
value_type: :integer
|
||||
})
|
||||
|> Ash.update(actor: actor)
|
||||
|
||||
# Verify error message contains expected text
|
||||
error_message = Exception.message(error)
|
||||
assert error_message =~ "cannot be changed" or error_message =~ "value_type"
|
||||
|
||||
# Reload and verify value_type remained unchanged
|
||||
reloaded = Ash.get!(CustomField, custom_field.id, actor: actor)
|
||||
assert reloaded.value_type == original_value_type
|
||||
assert reloaded.value_type == :string
|
||||
end
|
||||
|
||||
test "allows updating other fields while value_type remains unchanged", %{actor: actor} do
|
||||
# Create custom field with value_type :string
|
||||
{:ok, custom_field} =
|
||||
CustomField
|
||||
|> Ash.Changeset.for_create(:create, %{
|
||||
name: "test_field",
|
||||
value_type: :string,
|
||||
description: "Original description"
|
||||
})
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
original_value_type = custom_field.value_type
|
||||
assert original_value_type == :string
|
||||
|
||||
# Update other fields (name, description) without touching value_type
|
||||
{:ok, updated_custom_field} =
|
||||
custom_field
|
||||
|> Ash.Changeset.for_update(:update, %{
|
||||
name: "updated_name",
|
||||
description: "Updated description"
|
||||
})
|
||||
|> Ash.update(actor: actor)
|
||||
|
||||
# Verify value_type remained unchanged
|
||||
assert updated_custom_field.value_type == original_value_type
|
||||
assert updated_custom_field.value_type == :string
|
||||
# Verify other fields were updated
|
||||
assert updated_custom_field.name == "updated_name"
|
||||
assert updated_custom_field.description == "Updated description"
|
||||
end
|
||||
|
||||
test "rejects value_type change even when other fields are updated", %{actor: actor} do
|
||||
# Create custom field with value_type :boolean
|
||||
{:ok, custom_field} =
|
||||
CustomField
|
||||
|> Ash.Changeset.for_create(:create, %{
|
||||
name: "test_field",
|
||||
value_type: :boolean
|
||||
})
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
original_value_type = custom_field.value_type
|
||||
assert original_value_type == :boolean
|
||||
|
||||
# Attempt to update both name and value_type
|
||||
assert {:error, %Ash.Error.Invalid{} = error} =
|
||||
custom_field
|
||||
|> Ash.Changeset.for_update(:update, %{
|
||||
name: "updated_name",
|
||||
value_type: :date
|
||||
})
|
||||
|> Ash.update(actor: actor)
|
||||
|
||||
# Verify error message
|
||||
error_message = Exception.message(error)
|
||||
assert error_message =~ "cannot be changed" or error_message =~ "value_type"
|
||||
|
||||
# Reload and verify value_type remained unchanged, but name was not updated either
|
||||
reloaded = Ash.get!(CustomField, custom_field.id, actor: actor)
|
||||
assert reloaded.value_type == original_value_type
|
||||
assert reloaded.value_type == :boolean
|
||||
assert reloaded.name == "test_field"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
386
test/membership/member_search_groups_integration_test.exs
Normal file
386
test/membership/member_search_groups_integration_test.exs
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
defmodule Mv.Membership.MemberSearchGroupsIntegrationTest do
|
||||
@moduledoc """
|
||||
Tests for member search integration with group names (Issue #375).
|
||||
|
||||
Verifies that:
|
||||
- Group names are included in member search (via search_vector / FTS)
|
||||
- Searching by group name returns all members in that group
|
||||
- Search vector updates when member-group associations change (trigger on member_groups)
|
||||
- Edge cases (multiple groups, no groups, special characters) and authorization
|
||||
|
||||
Implementation: search_vector trigger and trigger on member_groups
|
||||
(see migration 20260217120000_add_group_names_to_member_search_vector.exs, Issue #375).
|
||||
"""
|
||||
use Mv.DataCase, async: false
|
||||
|
||||
alias Mv.Helpers.SystemActor
|
||||
alias Mv.Membership.{Group, Member, MemberGroup}
|
||||
|
||||
setup do
|
||||
system_actor = SystemActor.get_system_actor()
|
||||
%{system_actor: system_actor}
|
||||
end
|
||||
|
||||
describe "search by group name" do
|
||||
test "search by group name finds member in that group", %{system_actor: actor} do
|
||||
{:ok, member} =
|
||||
Mv.Membership.create_member(
|
||||
%{first_name: "Anna", last_name: "Arbeiter", email: "anna@example.com"},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, group} =
|
||||
Group
|
||||
|> Ash.Changeset.for_create(:create, %{name: "Vorstand"})
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
{:ok, _mg} =
|
||||
MemberGroup
|
||||
|> Ash.Changeset.for_create(:create, %{member_id: member.id, group_id: group.id})
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
results =
|
||||
Member
|
||||
|> Member.fuzzy_search(%{query: "Vorstand"})
|
||||
|> Ash.read!(actor: actor)
|
||||
|
||||
assert length(results) == 1
|
||||
assert List.first(results).id == member.id
|
||||
end
|
||||
|
||||
test "search by group name finds all members in that group", %{system_actor: actor} do
|
||||
{:ok, m1} =
|
||||
Mv.Membership.create_member(
|
||||
%{first_name: "Bob", last_name: "Brown", email: "bob1@example.com"},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, m2} =
|
||||
Mv.Membership.create_member(
|
||||
%{first_name: "Beth", last_name: "Blue", email: "beth@example.com"},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, group} =
|
||||
Group
|
||||
|> Ash.Changeset.for_create(:create, %{name: "Board Members"})
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
for member <- [m1, m2] do
|
||||
{:ok, _} =
|
||||
MemberGroup
|
||||
|> Ash.Changeset.for_create(:create, %{member_id: member.id, group_id: group.id})
|
||||
|> Ash.create(actor: actor)
|
||||
end
|
||||
|
||||
results =
|
||||
Member
|
||||
|> Member.fuzzy_search(%{query: "Board Members"})
|
||||
|> Ash.read!(actor: actor)
|
||||
|
||||
ids = Enum.map(results, & &1.id)
|
||||
assert m1.id in ids
|
||||
assert m2.id in ids
|
||||
assert length(results) == 2
|
||||
end
|
||||
|
||||
test "member in multiple groups is findable by any of those group names", %{
|
||||
system_actor: actor
|
||||
} do
|
||||
{:ok, member} =
|
||||
Mv.Membership.create_member(
|
||||
%{first_name: "Clara", last_name: "Clark", email: "clara@example.com"},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, g1} =
|
||||
Group
|
||||
|> Ash.Changeset.for_create(:create, %{name: "Alpha Team"})
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
{:ok, g2} =
|
||||
Group
|
||||
|> Ash.Changeset.for_create(:create, %{name: "Beta Team"})
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
for {m, g} <- [{member, g1}, {member, g2}] do
|
||||
{:ok, _} =
|
||||
MemberGroup
|
||||
|> Ash.Changeset.for_create(:create, %{member_id: m.id, group_id: g.id})
|
||||
|> Ash.create(actor: actor)
|
||||
end
|
||||
|
||||
for group_name <- ["Alpha Team", "Beta Team"] do
|
||||
results =
|
||||
Member
|
||||
|> Member.fuzzy_search(%{query: group_name})
|
||||
|> Ash.read!(actor: actor)
|
||||
|
||||
assert Enum.any?(results, fn r -> r.id == member.id end),
|
||||
"Search for #{group_name} should find member"
|
||||
end
|
||||
end
|
||||
|
||||
test "search by group name does not return members not in that group", %{
|
||||
system_actor: actor
|
||||
} do
|
||||
{:ok, member_in_x} =
|
||||
Mv.Membership.create_member(
|
||||
%{first_name: "Xavier", last_name: "X", email: "xavier@example.com"},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, member_in_y} =
|
||||
Mv.Membership.create_member(
|
||||
%{first_name: "Yvonne", last_name: "Y", email: "yvonne@example.com"},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, group_x} =
|
||||
Group
|
||||
|> Ash.Changeset.for_create(:create, %{name: "GroupXOnly"})
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
{:ok, group_y} =
|
||||
Group
|
||||
|> Ash.Changeset.for_create(:create, %{name: "GroupYOnly"})
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
{:ok, _} =
|
||||
MemberGroup
|
||||
|> Ash.Changeset.for_create(:create, %{member_id: member_in_x.id, group_id: group_x.id})
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
{:ok, _} =
|
||||
MemberGroup
|
||||
|> Ash.Changeset.for_create(:create, %{member_id: member_in_y.id, group_id: group_y.id})
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
results_x =
|
||||
Member
|
||||
|> Member.fuzzy_search(%{query: "GroupXOnly"})
|
||||
|> Ash.read!(actor: actor)
|
||||
|
||||
assert Enum.any?(results_x, fn r -> r.id == member_in_x.id end)
|
||||
refute Enum.any?(results_x, fn r -> r.id == member_in_y.id end)
|
||||
|
||||
results_y =
|
||||
Member
|
||||
|> Member.fuzzy_search(%{query: "GroupYOnly"})
|
||||
|> Ash.read!(actor: actor)
|
||||
|
||||
assert Enum.any?(results_y, fn r -> r.id == member_in_y.id end)
|
||||
refute Enum.any?(results_y, fn r -> r.id == member_in_x.id end)
|
||||
end
|
||||
|
||||
test "member with no groups is not found by unrelated group name", %{system_actor: actor} do
|
||||
{:ok, member} =
|
||||
Mv.Membership.create_member(
|
||||
%{first_name: "Solo", last_name: "User", email: "solo@example.com"},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, _group} =
|
||||
Group
|
||||
|> Ash.Changeset.for_create(:create, %{name: "SomeOtherGroup"})
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
# Member is not in any group; search for the group name should not return this member
|
||||
results =
|
||||
Member
|
||||
|> Member.fuzzy_search(%{query: "SomeOtherGroup"})
|
||||
|> Ash.read!(actor: actor)
|
||||
|
||||
refute Enum.any?(results, fn r -> r.id == member.id end)
|
||||
end
|
||||
end
|
||||
|
||||
describe "search vector update on member_groups changes" do
|
||||
test "adding member to group updates search vector (INSERT on member_groups)", %{
|
||||
system_actor: actor
|
||||
} do
|
||||
{:ok, member} =
|
||||
Mv.Membership.create_member(
|
||||
%{first_name: "New", last_name: "Member", email: "new@example.com"},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, group} =
|
||||
Group
|
||||
|> Ash.Changeset.for_create(:create, %{name: "NewlyAddedGroup"})
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
# Before adding to group, search should not find by group name
|
||||
results_before =
|
||||
Member
|
||||
|> Member.fuzzy_search(%{query: "NewlyAddedGroup"})
|
||||
|> Ash.read!(actor: actor)
|
||||
|
||||
refute Enum.any?(results_before, fn r -> r.id == member.id end)
|
||||
|
||||
{:ok, _mg} =
|
||||
MemberGroup
|
||||
|> Ash.Changeset.for_create(:create, %{member_id: member.id, group_id: group.id})
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
# After adding, search should find member (trigger on member_groups INSERT)
|
||||
results_after =
|
||||
Member
|
||||
|> Member.fuzzy_search(%{query: "NewlyAddedGroup"})
|
||||
|> Ash.read!(actor: actor)
|
||||
|
||||
assert Enum.any?(results_after, fn r -> r.id == member.id end)
|
||||
end
|
||||
|
||||
test "removing member from group updates search vector (DELETE on member_groups)", %{
|
||||
system_actor: actor
|
||||
} do
|
||||
# Use a member name that does not overlap with the group name so that the only
|
||||
# way to find them is via search_vector (group name). Otherwise trigram fuzzy
|
||||
# match on first_name would still find "Remove" when searching "RemovedGroup".
|
||||
{:ok, member} =
|
||||
Mv.Membership.create_member(
|
||||
%{first_name: "Zara", last_name: "None", email: "zara.remove@example.com"},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, group} =
|
||||
Group
|
||||
|> Ash.Changeset.for_create(:create, %{name: "RemovedGroup"})
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
{:ok, mg} =
|
||||
MemberGroup
|
||||
|> Ash.Changeset.for_create(:create, %{member_id: member.id, group_id: group.id})
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
results_before =
|
||||
Member
|
||||
|> Member.fuzzy_search(%{query: "RemovedGroup"})
|
||||
|> Ash.read!(actor: actor)
|
||||
|
||||
assert Enum.any?(results_before, fn r -> r.id == member.id end)
|
||||
|
||||
:ok = Mv.Membership.destroy_member_group(mg, actor: actor)
|
||||
|
||||
results_after =
|
||||
Member
|
||||
|> Member.fuzzy_search(%{query: "RemovedGroup"})
|
||||
|> Ash.read!(actor: actor)
|
||||
|
||||
refute Enum.any?(results_after, fn r -> r.id == member.id end)
|
||||
end
|
||||
end
|
||||
|
||||
describe "edge cases" do
|
||||
test "token match: single word in group name matches (e.g. Board in Board Members)", %{
|
||||
system_actor: actor
|
||||
} do
|
||||
{:ok, member} =
|
||||
Mv.Membership.create_member(
|
||||
%{first_name: "Partial", last_name: "Test", email: "partial@example.com"},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, group} =
|
||||
Group
|
||||
|> Ash.Changeset.for_create(:create, %{name: "Board Members"})
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
{:ok, _mg} =
|
||||
MemberGroup
|
||||
|> Ash.Changeset.for_create(:create, %{member_id: member.id, group_id: group.id})
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
# FTS with 'simple' config: full word "Board" or "Members" should match
|
||||
results =
|
||||
Member
|
||||
|> Member.fuzzy_search(%{query: "Board"})
|
||||
|> Ash.read!(actor: actor)
|
||||
|
||||
assert Enum.any?(results, fn r -> r.id == member.id end),
|
||||
"Search for 'Board' should find member in group 'Board Members'"
|
||||
end
|
||||
|
||||
test "search with token from group name containing special characters does not crash", %{
|
||||
system_actor: actor
|
||||
} do
|
||||
{:ok, member} =
|
||||
Mv.Membership.create_member(
|
||||
%{first_name: "Special", last_name: "Char", email: "special@example.com"},
|
||||
actor: actor
|
||||
)
|
||||
|
||||
{:ok, group} =
|
||||
Group
|
||||
|> Ash.Changeset.for_create(:create, %{name: "Team A&B"})
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
{:ok, _mg} =
|
||||
MemberGroup
|
||||
|> Ash.Changeset.for_create(:create, %{member_id: member.id, group_id: group.id})
|
||||
|> Ash.create(actor: actor)
|
||||
|
||||
# Search for a token from the group name; proves tokenization does not crash on "A&B"
|
||||
results =
|
||||
Member
|
||||
|> Member.fuzzy_search(%{query: "Team"})
|
||||
|> Ash.read!(actor: actor)
|
||||
|
||||
assert Enum.any?(results, fn r -> r.id == member.id end),
|
||||
"Search for 'Team' should find member in group 'Team A&B'"
|
||||
end
|
||||
end
|
||||
|
||||
describe "authorization" do
|
||||
test "search respects authorization (actor sees only allowed members)", %{
|
||||
system_actor: system_actor
|
||||
} do
|
||||
# own_data user linked to member1 can only read member1; member2 is in same group
|
||||
admin = Mv.Fixtures.user_with_role_fixture("admin")
|
||||
user_own_data = Mv.Fixtures.user_with_role_fixture("own_data")
|
||||
|
||||
member1 =
|
||||
Mv.Fixtures.member_fixture(%{
|
||||
first_name: "Linked",
|
||||
last_name: "User",
|
||||
email: "linked@example.com"
|
||||
})
|
||||
|
||||
member2 =
|
||||
Mv.Fixtures.member_fixture(%{
|
||||
first_name: "Other",
|
||||
last_name: "User",
|
||||
email: "other@example.com"
|
||||
})
|
||||
|
||||
{:ok, user_own_data} =
|
||||
user_own_data
|
||||
|> Ash.Changeset.for_update(:update, %{})
|
||||
|> Ash.Changeset.force_change_attribute(:member_id, member1.id)
|
||||
|> Ash.update(actor: admin)
|
||||
|
||||
{:ok, group} =
|
||||
Group
|
||||
|> Ash.Changeset.for_create(:create, %{name: "SharedGroupName"})
|
||||
|> Ash.create(actor: system_actor)
|
||||
|
||||
for member <- [member1, member2] do
|
||||
{:ok, _} =
|
||||
MemberGroup
|
||||
|> Ash.Changeset.for_create(:create, %{member_id: member.id, group_id: group.id})
|
||||
|> Ash.create(actor: admin)
|
||||
end
|
||||
|
||||
# Search as own_data user: should only return member1 (linked), not member2
|
||||
results =
|
||||
Member
|
||||
|> Member.fuzzy_search(%{query: "SharedGroupName"})
|
||||
|> Ash.read!(actor: user_own_data)
|
||||
|
||||
ids = Enum.map(results, & &1.id)
|
||||
assert member1.id in ids
|
||||
refute member2.id in ids
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -223,7 +223,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
end
|
||||
|
||||
describe "component behavior" do
|
||||
test "clicking sends sort message to parent", %{conn: conn} do
|
||||
test "clicking triggers sort event on parent LiveView", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members")
|
||||
|
||||
|
|
@ -232,7 +232,7 @@ defmodule MvWeb.Components.SortHeaderComponentTest do
|
|||
|> element("button[phx-value-field='first_name']")
|
||||
|> render_click()
|
||||
|
||||
# The component should send a message to the parent LiveView
|
||||
# The component triggers a "sort" event on the parent LiveView
|
||||
# This is tested indirectly through the URL change in integration tests
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -8,16 +8,19 @@ defmodule MvWeb.MemberLive.IndexGroupsIntegrationTest do
|
|||
- Groups sorting works with other sortings
|
||||
- Groups work with Membership Fee Status filter
|
||||
- Groups work with existing search (but not testing search integration itself)
|
||||
- Member index search by group name returns members in that group (Issue #375)
|
||||
"""
|
||||
# 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, CustomField, CustomFieldValue}
|
||||
alias Mv.Helpers.SystemActor
|
||||
alias Mv.Membership.{CustomField, CustomFieldValue, Group, MemberGroup}
|
||||
alias Mv.MembershipFees.{MembershipFeeCycle, MembershipFeeType}
|
||||
|
||||
setup do
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
system_actor = SystemActor.get_system_actor()
|
||||
|
||||
# Create test members
|
||||
{:ok, member1} =
|
||||
|
|
@ -80,15 +83,10 @@ defmodule MvWeb.MemberLive.IndexGroupsIntegrationTest do
|
|||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, _view, html} = live(conn, "/members")
|
||||
|
||||
# Verify groups column is visible by default
|
||||
# Verify groups column is visible by default (header and content)
|
||||
assert html =~ group1.name
|
||||
assert html =~ member1.first_name
|
||||
|
||||
# Hide groups column via field visibility dropdown
|
||||
# (This tests integration with field visibility feature)
|
||||
# Note: Actual implementation depends on how field visibility works
|
||||
# For now, we verify the column exists and can be toggled
|
||||
assert html
|
||||
assert html =~ "Groups"
|
||||
end
|
||||
|
||||
test "groups filter works with custom field filters", %{
|
||||
|
|
@ -140,11 +138,11 @@ defmodule MvWeb.MemberLive.IndexGroupsIntegrationTest do
|
|||
member1: member1,
|
||||
group1: group1
|
||||
} do
|
||||
system_actor = Mv.Helpers.SystemActor.get_system_actor()
|
||||
system_actor = SystemActor.get_system_actor()
|
||||
|
||||
# Create a membership fee type and cycle for member1
|
||||
{:ok, fee_type} =
|
||||
Mv.MembershipFees.MembershipFeeType
|
||||
MembershipFeeType
|
||||
|> Ash.Changeset.for_create(:create, %{
|
||||
name: "Test Fee",
|
||||
amount: Decimal.new("50.00"),
|
||||
|
|
@ -159,7 +157,7 @@ defmodule MvWeb.MemberLive.IndexGroupsIntegrationTest do
|
|||
)
|
||||
|
||||
{:ok, _cycle} =
|
||||
Mv.MembershipFees.MembershipFeeCycle
|
||||
MembershipFeeCycle
|
||||
|> Ash.Changeset.for_create(:create, %{
|
||||
member_id: member1.id,
|
||||
membership_fee_type_id: fee_type.id,
|
||||
|
|
@ -212,6 +210,25 @@ defmodule MvWeb.MemberLive.IndexGroupsIntegrationTest do
|
|||
# (that's part of Issue #5 - Search Integration)
|
||||
end
|
||||
|
||||
test "member index search by group name returns members in that group", %{
|
||||
conn: conn,
|
||||
member1: member1,
|
||||
member2: member2,
|
||||
group1: group1
|
||||
} do
|
||||
# member1 is in group1 "Board Members", member2 is not
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members")
|
||||
|
||||
view
|
||||
|> element("form[phx-submit='search']")
|
||||
|> render_submit(%{"query" => group1.name})
|
||||
|
||||
html = render(view)
|
||||
assert html =~ member1.first_name
|
||||
refute html =~ member2.first_name
|
||||
end
|
||||
|
||||
test "all filters and sortings work together", %{
|
||||
conn: conn,
|
||||
member1: member1,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
defmodule MvWeb.MemberLive.IndexTest do
|
||||
use MvWeb.ConnCase, async: true
|
||||
use MvWeb.ConnCase, async: false
|
||||
import Phoenix.LiveViewTest
|
||||
require Ash.Query
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue