test: add comprehensive tests for membership fee UI components
Add tests for all membership fee UI components following TDD principles:
This commit is contained in:
parent
bc989422e2
commit
5789079ab0
9 changed files with 1695 additions and 0 deletions
232
test/mv_web/member_live/show_membership_fees_test.exs
Normal file
232
test/mv_web/member_live/show_membership_fees_test.exs
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
defmodule MvWeb.MemberLive.ShowMembershipFeesTest do
|
||||
@moduledoc """
|
||||
Tests for membership fees section in member detail view.
|
||||
"""
|
||||
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
|
||||
|
||||
# Helper to create a cycle
|
||||
defp create_cycle(member, fee_type, attrs) do
|
||||
default_attrs = %{
|
||||
cycle_start: ~D[2023-01-01],
|
||||
amount: Decimal.new("50.00"),
|
||||
member_id: member.id,
|
||||
membership_fee_type_id: fee_type.id,
|
||||
status: :unpaid
|
||||
}
|
||||
|
||||
attrs = Map.merge(default_attrs, attrs)
|
||||
|
||||
MembershipFeeCycle
|
||||
|> Ash.Changeset.for_create(:create, attrs)
|
||||
|> Ash.create!()
|
||||
end
|
||||
|
||||
describe "cycles table display" do
|
||||
test "displays all cycles for member", %{conn: conn} do
|
||||
fee_type = create_fee_type(%{interval: :yearly})
|
||||
member = create_member(%{membership_fee_type_id: fee_type.id})
|
||||
|
||||
_cycle1 = create_cycle(member, fee_type, %{cycle_start: ~D[2022-01-01], status: :paid})
|
||||
_cycle2 = create_cycle(member, fee_type, %{cycle_start: ~D[2023-01-01], status: :unpaid})
|
||||
|
||||
{:ok, _view, html} = live(conn, "/members/#{member.id}")
|
||||
|
||||
# Should show cycles table
|
||||
assert html =~ "Membership Fees" || html =~ "Mitgliedsbeiträge"
|
||||
assert html =~ "2022" || html =~ "2023"
|
||||
end
|
||||
|
||||
test "table columns show correct data", %{conn: conn} do
|
||||
fee_type = create_fee_type(%{interval: :yearly, amount: Decimal.new("60.00")})
|
||||
member = create_member(%{membership_fee_type_id: fee_type.id})
|
||||
|
||||
create_cycle(member, fee_type, %{
|
||||
cycle_start: ~D[2023-01-01],
|
||||
amount: Decimal.new("60.00"),
|
||||
status: :paid
|
||||
})
|
||||
|
||||
{:ok, _view, html} = live(conn, "/members/#{member.id}")
|
||||
|
||||
# Should show interval, amount, status
|
||||
assert html =~ "Yearly" || html =~ "Jährlich"
|
||||
assert html =~ "60" || html =~ "60,00"
|
||||
assert html =~ "paid" || html =~ "bezahlt"
|
||||
end
|
||||
end
|
||||
|
||||
describe "membership fee type dropdown" do
|
||||
test "shows only same-interval types", %{conn: conn} do
|
||||
yearly_type = create_fee_type(%{interval: :yearly, name: "Yearly Type"})
|
||||
_monthly_type = create_fee_type(%{interval: :monthly, name: "Monthly Type"})
|
||||
|
||||
member = create_member(%{membership_fee_type_id: yearly_type.id})
|
||||
|
||||
{:ok, _view, html} = live(conn, "/members/#{member.id}")
|
||||
|
||||
# Should show yearly type but not monthly
|
||||
assert html =~ "Yearly Type"
|
||||
refute html =~ "Monthly Type"
|
||||
end
|
||||
|
||||
test "shows warning if different interval selected", %{conn: conn} do
|
||||
yearly_type = create_fee_type(%{interval: :yearly, name: "Yearly Type"})
|
||||
monthly_type = create_fee_type(%{interval: :monthly, name: "Monthly Type"})
|
||||
|
||||
member = create_member(%{membership_fee_type_id: yearly_type.id})
|
||||
|
||||
{:ok, view, _html} = live(conn, "/members/#{member.id}")
|
||||
|
||||
# Try to select monthly type (should show warning)
|
||||
# Note: This test may need adjustment based on actual implementation
|
||||
html =
|
||||
view
|
||||
|> form("form", %{"membership_fee_type_id" => monthly_type.id})
|
||||
|> render_change()
|
||||
|
||||
assert html =~ "Warning" || html =~ "Warnung" || html =~ "not allowed"
|
||||
end
|
||||
end
|
||||
|
||||
describe "status change actions" do
|
||||
test "mark as paid works", %{conn: conn} do
|
||||
fee_type = create_fee_type(%{interval: :yearly})
|
||||
member = create_member(%{membership_fee_type_id: fee_type.id})
|
||||
|
||||
cycle = create_cycle(member, fee_type, %{cycle_start: ~D[2023-01-01], status: :unpaid})
|
||||
|
||||
{:ok, view, _html} = live(conn, "/members/#{member.id}")
|
||||
|
||||
# Mark as paid
|
||||
view
|
||||
|> element("button[phx-click='mark_as_paid'][phx-value-cycle-id='#{cycle.id}']")
|
||||
|> render_click()
|
||||
|
||||
# Verify cycle is now paid
|
||||
updated_cycle = Ash.read_one!(MembershipFeeCycle |> Ash.Query.filter(id == ^cycle.id))
|
||||
assert updated_cycle.status == :paid
|
||||
end
|
||||
|
||||
test "mark as suspended works", %{conn: conn} do
|
||||
fee_type = create_fee_type(%{interval: :yearly})
|
||||
member = create_member(%{membership_fee_type_id: fee_type.id})
|
||||
|
||||
cycle = create_cycle(member, fee_type, %{cycle_start: ~D[2023-01-01], status: :unpaid})
|
||||
|
||||
{:ok, view, _html} = live(conn, "/members/#{member.id}")
|
||||
|
||||
# Mark as suspended
|
||||
view
|
||||
|> element("button[phx-click='mark_as_suspended'][phx-value-cycle-id='#{cycle.id}']")
|
||||
|> render_click()
|
||||
|
||||
# Verify cycle is now suspended
|
||||
updated_cycle = Ash.read_one!(MembershipFeeCycle |> Ash.Query.filter(id == ^cycle.id))
|
||||
assert updated_cycle.status == :suspended
|
||||
end
|
||||
|
||||
test "mark as unpaid works", %{conn: conn} do
|
||||
fee_type = create_fee_type(%{interval: :yearly})
|
||||
member = create_member(%{membership_fee_type_id: fee_type.id})
|
||||
|
||||
cycle = create_cycle(member, fee_type, %{cycle_start: ~D[2023-01-01], status: :paid})
|
||||
|
||||
{:ok, view, _html} = live(conn, "/members/#{member.id}")
|
||||
|
||||
# Mark as unpaid
|
||||
view
|
||||
|> element("button[phx-click='mark_as_unpaid'][phx-value-cycle-id='#{cycle.id}']")
|
||||
|> render_click()
|
||||
|
||||
# Verify cycle is now unpaid
|
||||
updated_cycle = Ash.read_one!(MembershipFeeCycle |> Ash.Query.filter(id == ^cycle.id))
|
||||
assert updated_cycle.status == :unpaid
|
||||
end
|
||||
end
|
||||
|
||||
describe "cycle regeneration" do
|
||||
test "manual regeneration works", %{conn: conn} do
|
||||
fee_type = create_fee_type(%{interval: :yearly})
|
||||
member = create_member(%{membership_fee_type_id: fee_type.id})
|
||||
|
||||
{:ok, view, _html} = live(conn, "/members/#{member.id}")
|
||||
|
||||
# Trigger regeneration
|
||||
view
|
||||
|> element("button[phx-click='regenerate_cycles']")
|
||||
|> render_click()
|
||||
|
||||
# Should have cycles generated
|
||||
cycles =
|
||||
MembershipFeeCycle
|
||||
|> Ash.Query.filter(member_id == ^member.id)
|
||||
|> Ash.read!()
|
||||
|
||||
assert length(cycles) > 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "edge cases" do
|
||||
test "handles members without membership fee type gracefully", %{conn: conn} do
|
||||
# No fee type
|
||||
member = create_member(%{})
|
||||
|
||||
{:ok, _view, html} = live(conn, "/members/#{member.id}")
|
||||
|
||||
# Should not crash
|
||||
assert html =~ member.first_name
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue