Merge branch 'main' into feature/223_memberfields_settings

This commit is contained in:
carla 2026-01-07 11:11:02 +01:00
commit 909d4af2a2
121 changed files with 20360 additions and 2522 deletions

View file

@ -0,0 +1,218 @@
defmodule MvWeb.MembershipFeeTypeLive.FormTest do
@moduledoc """
Tests for membership fee types create/edit form.
"""
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
alias Mv.MembershipFees.MembershipFeeType
alias Mv.Membership.Member
require Ash.Query
setup %{conn: conn} do
# Create admin user
{:ok, user} =
Mv.Accounts.User
|> Ash.Changeset.for_create(:register_with_password, %{
email: "admin#{System.unique_integer([:positive])}@mv.local",
password: "testpassword123"
})
|> Ash.create()
authenticated_conn = conn_with_password_user(conn, user)
%{conn: authenticated_conn, user: user}
end
# Helper to create a membership fee type
defp create_fee_type(attrs) do
default_attrs = %{
name: "Test Fee Type #{System.unique_integer([:positive])}",
amount: Decimal.new("50.00"),
interval: :yearly
}
attrs = Map.merge(default_attrs, attrs)
MembershipFeeType
|> Ash.Changeset.for_create(:create, attrs)
|> Ash.create!()
end
# Helper to create a member
defp create_member(attrs) do
default_attrs = %{
first_name: "Test",
last_name: "Member",
email: "test.member.#{System.unique_integer([:positive])}@example.com"
}
attrs = Map.merge(default_attrs, attrs)
Member
|> Ash.Changeset.for_create(:create_member, attrs)
|> Ash.create!()
end
describe "create form" do
test "creates new membership fee type", %{conn: conn} do
{:ok, view, _html} = live(conn, "/membership_fee_types/new")
form_data = %{
"membership_fee_type[name]" => "New Type",
"membership_fee_type[amount]" => "75.00",
"membership_fee_type[interval]" => "yearly",
"membership_fee_type[description]" => "Test description"
}
{:error, {:live_redirect, %{to: to}}} =
view
|> form("#membership-fee-type-form", form_data)
|> render_submit()
assert to == "/membership_fee_types"
# Verify type was created
type =
MembershipFeeType
|> Ash.Query.filter(name == "New Type")
|> Ash.read_one!()
assert type.amount == Decimal.new("75.00")
assert type.interval == :yearly
end
test "interval field is editable on create", %{conn: conn} do
{:ok, _view, html} = live(conn, "/membership_fee_types/new")
# Interval field should be editable (not disabled)
refute html =~ "disabled" || html =~ "readonly"
end
end
describe "edit form" do
test "loads existing type data", %{conn: conn} do
fee_type = create_fee_type(%{name: "Existing Type", amount: Decimal.new("60.00")})
{:ok, _view, html} = live(conn, "/membership_fee_types/#{fee_type.id}/edit")
assert html =~ "Existing Type"
assert html =~ "60" || html =~ "60,00"
end
test "interval field is grayed out on edit", %{conn: conn} do
fee_type = create_fee_type(%{interval: :yearly})
{:ok, _view, html} = live(conn, "/membership_fee_types/#{fee_type.id}/edit")
# Interval field should be disabled
assert html =~ "disabled" || html =~ "readonly"
end
test "amount change warning displays on edit", %{conn: conn} do
fee_type = create_fee_type(%{amount: Decimal.new("50.00")})
create_member(%{membership_fee_type_id: fee_type.id})
{:ok, view, _html} = live(conn, "/membership_fee_types/#{fee_type.id}/edit")
# Change amount
view
|> form("#membership-fee-type-form", %{"membership_fee_type[amount]" => "75.00"})
|> render_change()
# Should show warning in rendered view
html = render(view)
assert html =~ "affect" || html =~ "Change Amount"
end
test "amount change warning shows correct affected member count", %{conn: conn} do
fee_type = create_fee_type(%{amount: Decimal.new("50.00")})
# Create 3 members
Enum.each(1..3, fn _ ->
create_member(%{membership_fee_type_id: fee_type.id})
end)
{:ok, view, _html} = live(conn, "/membership_fee_types/#{fee_type.id}/edit")
# Change amount
html =
view
|> form("#membership-fee-type-form", %{"membership_fee_type[amount]" => "75.00"})
|> render_change()
# Should show affected count
assert html =~ "3" || html =~ "members" || html =~ "Mitglieder"
end
test "amount change can be confirmed", %{conn: conn} do
fee_type = create_fee_type(%{amount: Decimal.new("50.00")})
{:ok, view, _html} = live(conn, "/membership_fee_types/#{fee_type.id}/edit")
# Change amount and confirm
view
|> form("#membership-fee-type-form", %{"membership_fee_type[amount]" => "75.00"})
|> render_change()
view
|> element("button[phx-click='confirm_amount_change']")
|> render_click()
# Submit the form to actually save the change
view
|> form("#membership-fee-type-form", %{"membership_fee_type[amount]" => "75.00"})
|> render_submit()
# Amount should be updated
updated_type = Ash.read_one!(MembershipFeeType |> Ash.Query.filter(id == ^fee_type.id))
assert updated_type.amount == Decimal.new("75.00")
end
test "amount change can be cancelled", %{conn: conn} do
fee_type = create_fee_type(%{amount: Decimal.new("50.00")})
{:ok, view, _html} = live(conn, "/membership_fee_types/#{fee_type.id}/edit")
# Change amount and cancel
view
|> form("#membership-fee-type-form", %{"membership_fee_type[amount]" => "75.00"})
|> render_change()
view
|> element("button[phx-click='cancel_amount_change']")
|> render_click()
# Amount should remain unchanged
updated_type = Ash.read_one!(MembershipFeeType |> Ash.Query.filter(id == ^fee_type.id))
assert updated_type.amount == Decimal.new("50.00")
end
test "validation errors display correctly", %{conn: conn} do
{:ok, view, _html} = live(conn, "/membership_fee_types/new")
# Submit with invalid data
html =
view
|> form("#membership-fee-type-form", %{
"membership_fee_type[name]" => "",
"membership_fee_type[amount]" => ""
})
|> render_submit()
# Should show validation errors
assert html =~ "can't be blank" || html =~ "darf nicht leer sein" || html =~ "required"
end
end
describe "permissions" do
test "only admin can access", %{conn: conn} do
# This test assumes non-admin users cannot access
{:ok, _view, html} = live(conn, "/membership_fee_types/new")
# Should show the form (admin user in setup)
assert html =~ "Membership Fee Type" || html =~ "Beitragsart"
end
end
end

View file

@ -0,0 +1,151 @@
defmodule MvWeb.MembershipFeeTypeLive.IndexTest do
@moduledoc """
Tests for membership fee types list view.
"""
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
alias Mv.MembershipFees.MembershipFeeType
alias Mv.Membership.Member
require Ash.Query
setup %{conn: conn} do
# Create admin user
{:ok, user} =
Mv.Accounts.User
|> Ash.Changeset.for_create(:register_with_password, %{
email: "admin#{System.unique_integer([:positive])}@mv.local",
password: "testpassword123"
})
|> Ash.create()
authenticated_conn = conn_with_password_user(conn, user)
%{conn: authenticated_conn, user: user}
end
# Helper to create a membership fee type
defp create_fee_type(attrs) do
default_attrs = %{
name: "Test Fee Type #{System.unique_integer([:positive])}",
amount: Decimal.new("50.00"),
interval: :yearly
}
attrs = Map.merge(default_attrs, attrs)
MembershipFeeType
|> Ash.Changeset.for_create(:create, attrs)
|> Ash.create!()
end
# Helper to create a member
defp create_member(attrs) do
default_attrs = %{
first_name: "Test",
last_name: "Member",
email: "test.member.#{System.unique_integer([:positive])}@example.com"
}
attrs = Map.merge(default_attrs, attrs)
Member
|> Ash.Changeset.for_create(:create_member, attrs)
|> Ash.create!()
end
describe "list display" do
test "displays all membership fee types with correct data", %{conn: conn} do
_fee_type1 =
create_fee_type(%{name: "Regular", amount: Decimal.new("60.00"), interval: :yearly})
_fee_type2 =
create_fee_type(%{name: "Reduced", amount: Decimal.new("30.00"), interval: :yearly})
{:ok, _view, html} = live(conn, "/membership_fee_types")
assert html =~ "Regular"
assert html =~ "Reduced"
assert html =~ "60" || html =~ "60,00"
assert html =~ "30" || html =~ "30,00"
assert html =~ "Yearly" || html =~ "Jährlich"
end
test "member count column shows correct count", %{conn: conn} do
fee_type = create_fee_type(%{interval: :yearly})
# Create 3 members with this fee type
Enum.each(1..3, fn _ ->
create_member(%{membership_fee_type_id: fee_type.id})
end)
{:ok, _view, html} = live(conn, "/membership_fee_types")
assert html =~ "3" || html =~ "Members" || html =~ "Mitglieder"
end
test "create button navigates to form", %{conn: conn} do
{:ok, view, _html} = live(conn, "/membership_fee_types")
{:error, {:live_redirect, %{to: to}}} =
view
|> element("a[href='/membership_fee_types/new']")
|> render_click()
assert to == "/membership_fee_types/new"
end
test "edit button per row navigates to edit form", %{conn: conn} do
fee_type = create_fee_type(%{interval: :yearly})
{:ok, view, _html} = live(conn, "/membership_fee_types")
{:error, {:live_redirect, %{to: to}}} =
view
|> element("a[href='/membership_fee_types/#{fee_type.id}/edit']")
|> render_click()
assert to == "/membership_fee_types/#{fee_type.id}/edit"
end
end
describe "delete functionality" do
test "delete button disabled if type is in use", %{conn: conn} do
fee_type = create_fee_type(%{interval: :yearly})
create_member(%{membership_fee_type_id: fee_type.id})
{:ok, _view, html} = live(conn, "/membership_fee_types")
# Delete button should be disabled
assert html =~ "disabled" || html =~ "cursor-not-allowed"
end
test "delete button works if type is not in use", %{conn: conn} do
fee_type = create_fee_type(%{interval: :yearly})
# No members assigned
{:ok, view, _html} = live(conn, "/membership_fee_types")
# Delete button should be enabled
view
|> element("button[phx-click='delete'][phx-value-id='#{fee_type.id}']")
|> render_click()
# Type should be deleted
assert {:error, %Ash.Error.Invalid{errors: [%Ash.Error.Query.NotFound{}]}} =
Ash.get(MembershipFeeType, fee_type.id, domain: Mv.MembershipFees)
end
end
describe "permissions" do
test "only admin can access", %{conn: conn} do
# This test assumes non-admin users cannot access
# Adjust based on actual permission implementation
{:ok, _view, html} = live(conn, "/membership_fee_types")
# Should show the page (admin user in setup)
assert html =~ "Membership Fee Types" || html =~ "Beitragsarten"
end
end
end