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
210
test/mv_web/live/membership_fee_type_live/form_test.exs
Normal file
210
test/mv_web/live/membership_fee_type_live/form_test.exs
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
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 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 "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("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
|
||||
html =
|
||||
view
|
||||
|> form("form", %{"membership_fee_type[amount]" => "75.00"})
|
||||
|> render_change()
|
||||
|
||||
# Should show warning
|
||||
assert html =~ "Warning" || html =~ "Warnung" || html =~ "affected"
|
||||
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("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("form", %{"membership_fee_type[amount]" => "75.00"})
|
||||
|> render_change()
|
||||
|
||||
view
|
||||
|> element("button[phx-click='confirm_amount_change']")
|
||||
|> render_click()
|
||||
|
||||
# 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("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("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
|
||||
152
test/mv_web/live/membership_fee_type_live/index_test.exs
Normal file
152
test/mv_web/live/membership_fee_type_live/index_test.exs
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
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 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 "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_raise Ash.Error.Query.NotFound, fn ->
|
||||
Ash.read_one!(MembershipFeeType |> Ash.Query.filter(id == ^fee_type.id))
|
||||
end
|
||||
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