Merge branch 'main' into feature/223_memberfields_settings
This commit is contained in:
commit
909d4af2a2
121 changed files with 20360 additions and 2522 deletions
218
test/mv_web/live/membership_fee_type_live/form_test.exs
Normal file
218
test/mv_web/live/membership_fee_type_live/form_test.exs
Normal 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
|
||||
151
test/mv_web/live/membership_fee_type_live/index_test.exs
Normal file
151
test/mv_web/live/membership_fee_type_live/index_test.exs
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue