mitgliederverwaltung/test/mv_web/member_live/membership_fee_integration_test.exs
Moritz 5789079ab0
test: add comprehensive tests for membership fee UI components
Add tests for all membership fee UI components following TDD principles:
2025-12-18 15:08:32 +01:00

221 lines
6.4 KiB
Elixir

defmodule MvWeb.MemberLive.MembershipFeeIntegrationTest do
@moduledoc """
Integration tests for membership fee UI workflows.
"""
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
alias Mv.Membership.Member
alias Mv.MembershipFees.MembershipFeeType
alias Mv.MembershipFees.MembershipFeeCycle
require Ash.Query
setup 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()
conn = log_in_user(build_conn(), user)
%{conn: 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 "end-to-end workflows" do
test "create type → assign to member → view cycles → change status", %{conn: conn} do
# Create type
fee_type = create_fee_type(%{name: "Regular", interval: :yearly})
# Assign to member
member = create_member(%{membership_fee_type_id: fee_type.id})
# View cycles
{:ok, view, html} = live(conn, "/members/#{member.id}")
assert html =~ "Membership Fees" || html =~ "Mitgliedsbeiträge"
# Get a cycle
cycles =
MembershipFeeCycle
|> Ash.Query.filter(member_id == ^member.id)
|> Ash.read!()
if length(cycles) > 0 do
cycle = List.first(cycles)
# Change status
view
|> element("button[phx-click='mark_as_paid'][phx-value-cycle-id='#{cycle.id}']")
|> render_click()
# Verify status changed
updated_cycle = Ash.read_one!(MembershipFeeCycle |> Ash.Query.filter(id == ^cycle.id))
assert updated_cycle.status == :paid
end
end
test "change member type → cycles regenerate", %{conn: conn} do
fee_type1 =
create_fee_type(%{name: "Type 1", interval: :yearly, amount: Decimal.new("50.00")})
fee_type2 =
create_fee_type(%{name: "Type 2", interval: :yearly, amount: Decimal.new("75.00")})
member = create_member(%{membership_fee_type_id: fee_type1.id})
# Change type
{:ok, view, _html} = live(conn, "/members/#{member.id}/edit")
view
|> form("form", %{"member[membership_fee_type_id]" => fee_type2.id})
|> render_submit()
# Verify cycles regenerated with new amount
cycles =
MembershipFeeCycle
|> Ash.Query.filter(member_id == ^member.id)
|> Ash.Query.filter(status == :unpaid)
|> Ash.read!()
# Future unpaid cycles should have new amount
Enum.each(cycles, fn cycle ->
if Date.compare(cycle.cycle_start, Date.utc_today()) != :lt do
assert Decimal.equal?(cycle.amount, fee_type2.amount)
end
end)
end
test "update settings → new members get default type", %{conn: conn} do
fee_type = create_fee_type(%{interval: :yearly})
# Update settings
Mv.Membership.Setting
|> Ash.Changeset.for_update(:update_membership_fee_settings, %{
default_membership_fee_type_id: fee_type.id
})
|> Ash.update!()
# Create new member
{:ok, view, _html} = live(conn, "/members/new")
form_data = %{
"member[first_name]" => "New",
"member[last_name]" => "Member",
"member[email]" => "new#{System.unique_integer([:positive])}@example.com"
}
{:error, {:live_redirect, %{to: _to}}} =
view
|> form("form", form_data)
|> render_submit()
# Verify member got default type
member =
Member
|> Ash.Query.filter(email == ^form_data["member[email]"])
|> Ash.read_one!()
assert member.membership_fee_type_id == fee_type.id
end
test "delete cycle → confirmation → cycle deleted", %{conn: conn} do
fee_type = create_fee_type(%{interval: :yearly})
member = create_member(%{membership_fee_type_id: fee_type.id})
cycle =
MembershipFeeCycle
|> Ash.Changeset.for_create(:create, %{
cycle_start: ~D[2023-01-01],
amount: Decimal.new("50.00"),
member_id: member.id,
membership_fee_type_id: fee_type.id,
status: :unpaid
})
|> Ash.create!()
{:ok, view, _html} = live(conn, "/members/#{member.id}")
# Delete cycle with confirmation
view
|> element("button[phx-click='delete_cycle'][phx-value-cycle-id='#{cycle.id}']")
|> render_click()
# Confirm deletion
view
|> element("button[phx-click='confirm_delete_cycle'][phx-value-cycle-id='#{cycle.id}']")
|> render_click()
# Verify cycle deleted
assert_raise Ash.Error.Query.NotFound, fn ->
Ash.read_one!(MembershipFeeCycle |> Ash.Query.filter(id == ^cycle.id))
end
end
test "edit cycle amount → modal → amount updated", %{conn: conn} do
fee_type = create_fee_type(%{interval: :yearly})
member = create_member(%{membership_fee_type_id: fee_type.id})
cycle =
MembershipFeeCycle
|> Ash.Changeset.for_create(:create, %{
cycle_start: ~D[2023-01-01],
amount: Decimal.new("50.00"),
member_id: member.id,
membership_fee_type_id: fee_type.id,
status: :unpaid
})
|> Ash.create!()
{:ok, view, _html} = live(conn, "/members/#{member.id}")
# Open edit modal
view
|> element("button[phx-click='edit_cycle_amount'][phx-value-cycle-id='#{cycle.id}']")
|> render_click()
# Update amount
view
|> form("form", %{"amount" => "75.00"})
|> render_submit()
# Verify amount updated
updated_cycle = Ash.read_one!(MembershipFeeCycle |> Ash.Query.filter(id == ^cycle.id))
assert updated_cycle.amount == Decimal.new("75.00")
end
end
end