mitgliederverwaltung/test/mv_web/live/custom_field_live/deletion_test.exs
Moritz 4473cfd372
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone/promote/production Build is passing
Tests: use code interface for Member create/update (actor propagation)
2026-01-29 16:10:12 +01:00

317 lines
10 KiB
Elixir

defmodule MvWeb.CustomFieldLive.DeletionTest do
@moduledoc """
Tests for CustomFieldLive.IndexComponent deletion modal and slug confirmation.
Tests the custom field management component embedded in the settings page.
Tests cover:
- Opening deletion confirmation modal
- Displaying correct member count
- Slug confirmation input
- Successful deletion with correct slug
- Failed deletion with incorrect slug
- Canceling deletion
- Button states (enabled/disabled based on slug match)
"""
use MvWeb.ConnCase, async: true
import Phoenix.LiveViewTest
require Ash.Query
alias Mv.Membership.{CustomField, CustomFieldValue, Member}
setup do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
admin_role = Mv.Fixtures.role_fixture("admin")
# Create admin user for testing (must have admin role to read/manage CustomField)
{:ok, user} =
Mv.Accounts.User
|> Ash.Changeset.for_create(:register_with_password, %{
email: "admin#{System.unique_integer([:positive])}@mv.local",
password: "testpassword123"
})
|> Ash.create(actor: system_actor)
{:ok, user} =
user
|> Ash.Changeset.for_update(:update, %{})
|> Ash.Changeset.manage_relationship(:role, admin_role, type: :append_and_remove)
|> Ash.update(actor: system_actor)
user_with_role = Ash.load!(user, :role, domain: Mv.Accounts, actor: system_actor)
conn = log_in_user(build_conn(), user_with_role)
# Use English locale so "Delete" link text matches in assertions
session = conn.private[:plug_session] || %{}
conn = Plug.Test.init_test_session(conn, Map.put(session, "locale", "en"))
%{conn: conn, user: user_with_role}
end
describe "delete button and modal" do
test "opens modal with correct member count when delete is clicked", %{conn: conn} do
{:ok, member} = create_member()
{:ok, custom_field} = create_custom_field("test_field", :string)
# Create custom field value
create_custom_field_value(member, custom_field, "test")
{:ok, view, _html} = live(conn, ~p"/settings")
# Click delete button - find the delete link within the component
view
|> element("#custom-fields-component a", "Delete")
|> render_click()
# Modal should be visible
assert has_element?(view, "#delete-custom-field-modal")
# Should show correct member count (1 member)
assert render(view) =~ "1 member has a value assigned for this custom field"
# Should show the slug
assert render(view) =~ custom_field.slug
end
test "shows correct plural form for multiple members", %{conn: conn} do
{:ok, member1} = create_member()
{:ok, member2} = create_member()
{:ok, custom_field} = create_custom_field("test_field", :string)
# Create values for both members
create_custom_field_value(member1, custom_field, "test1")
create_custom_field_value(member2, custom_field, "test2")
{:ok, view, _html} = live(conn, ~p"/settings")
view
|> element("#custom-fields-component a", "Delete")
|> render_click()
# Should show plural form
assert render(view) =~ "2 members have values assigned for this custom field"
end
test "shows 0 members for custom field without values", %{conn: conn} do
{:ok, _custom_field} = create_custom_field("test_field", :string)
{:ok, view, _html} = live(conn, ~p"/settings")
view
|> element("#custom-fields-component a", "Delete")
|> render_click()
# Should show 0 members
assert render(view) =~ "0 members have values assigned for this custom field"
end
end
describe "slug confirmation input" do
test "updates confirmation state when typing", %{conn: conn} do
{:ok, custom_field} = create_custom_field("test_field", :string)
{:ok, view, _html} = live(conn, ~p"/settings")
view
|> element("#custom-fields-component a", "Delete")
|> render_click()
# Type in slug input - use element to find the form with phx-target
view
|> element("#delete-custom-field-modal form")
|> render_change(%{"slug" => custom_field.slug})
# Confirm button should be enabled now (no disabled attribute on the confirm button)
refute has_element?(view, "#delete-custom-field-modal button.btn-error[disabled]")
end
test "delete button is disabled when slug doesn't match", %{conn: conn} do
{:ok, _custom_field} = create_custom_field("test_field", :string)
{:ok, view, _html} = live(conn, ~p"/settings")
view
|> element("#custom-fields-component a", "Delete")
|> render_click()
# Type wrong slug - use element to find the form with phx-target
view
|> element("#delete-custom-field-modal form")
|> render_change(%{"slug" => "wrong-slug"})
# Confirm button should be disabled
assert has_element?(view, "#delete-custom-field-modal button.btn-error[disabled]")
end
end
describe "confirm deletion" do
test "successfully deletes custom field with correct slug", %{conn: conn} do
{:ok, member} = create_member()
{:ok, custom_field} = create_custom_field("test_field", :string)
{:ok, custom_field_value} = create_custom_field_value(member, custom_field, "test")
{:ok, view, _html} = live(conn, ~p"/settings")
# Open modal
view
|> element("#custom-fields-component a", "Delete")
|> render_click()
# Enter correct slug - use element to find the form with phx-target
view
|> element("#delete-custom-field-modal form")
|> render_change(%{"slug" => custom_field.slug})
# Click confirm
view
|> element("#delete-custom-field-modal button", "Delete Custom Field and All Values")
|> render_click()
# Should show success message
assert render(view) =~ "Data field deleted successfully"
system_actor = Mv.Helpers.SystemActor.get_system_actor()
# Custom field should be gone from database
assert {:error, _} = Ash.get(CustomField, custom_field.id, actor: system_actor)
# Custom field value should also be gone (CASCADE)
assert {:error, _} = Ash.get(CustomFieldValue, custom_field_value.id, actor: system_actor)
# Member should still exist
assert {:ok, _} = Ash.get(Member, member.id, actor: system_actor)
end
test "button remains disabled and custom field not deleted when slug doesn't match", %{
conn: conn
} do
{:ok, custom_field} = create_custom_field("test_field", :string)
{:ok, view, _html} = live(conn, ~p"/settings")
view
|> element("#custom-fields-component a", "Delete")
|> render_click()
# Enter wrong slug - use element to find the form with phx-target
view
|> element("#delete-custom-field-modal form")
|> render_change(%{"slug" => "wrong-slug"})
# Confirm button should be disabled and we cannot click it
assert has_element?(view, "#delete-custom-field-modal button.btn-error[disabled]")
# Custom field should still exist since deletion couldn't proceed
system_actor = Mv.Helpers.SystemActor.get_system_actor()
assert {:ok, _} = Ash.get(CustomField, custom_field.id, actor: system_actor)
end
end
describe "cancel deletion" do
test "closes modal without deleting", %{conn: conn} do
{:ok, custom_field} = create_custom_field("test_field", :string)
{:ok, view, _html} = live(conn, ~p"/settings")
view
|> element("#custom-fields-component a", "Delete")
|> render_click()
# Modal should be visible
assert has_element?(view, "#delete-custom-field-modal")
# Click cancel
view
|> element("#delete-custom-field-modal button", "Cancel")
|> render_click()
# Modal should be gone
refute has_element?(view, "#delete-custom-field-modal")
# Custom field should still exist
system_actor = Mv.Helpers.SystemActor.get_system_actor()
assert {:ok, _} = Ash.get(CustomField, custom_field.id, actor: system_actor)
end
end
describe "create custom field" do
test "submitting new data field form creates custom field and shows success", %{conn: conn} do
{:ok, view, _html} = live(conn, ~p"/settings")
# Open "New Data Field" form
view
|> element("#custom-fields-component button", "New Data Field")
|> render_click()
# Form is visible; submit with valid data
form_params = %{
"custom_field" => %{
"name" => "Created via Form",
"value_type" => "string",
"description" => "",
"required" => "false",
"show_in_overview" => "true"
}
}
view
|> form("#custom-field-form-new-form", form_params)
|> render_submit()
# Success flash (FormComponent needs actor from parent; without it KeyError would occur)
assert render(view) =~ "successfully"
# Custom field was created in DB
system_actor = Mv.Helpers.SystemActor.get_system_actor()
search_name = "Created via Form"
[custom_field] =
Mv.Membership.CustomField
|> Ash.Query.filter(name == ^search_name)
|> Ash.read!(actor: system_actor)
assert custom_field.value_type == :string
end
end
# Helper functions (use code interface so actor is in validation context)
defp create_member do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
Mv.Membership.create_member(
%{
first_name: "Test",
last_name: "User#{System.unique_integer([:positive])}",
email: "test#{System.unique_integer([:positive])}@example.com"
},
actor: system_actor
)
end
defp create_custom_field(name, value_type) do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
CustomField
|> Ash.Changeset.for_create(:create, %{
name: "#{name}_#{System.unique_integer([:positive])}",
value_type: value_type
})
|> Ash.create(actor: system_actor)
end
defp create_custom_field_value(member, custom_field, value) do
system_actor = Mv.Helpers.SystemActor.get_system_actor()
CustomFieldValue
|> Ash.Changeset.for_create(:create, %{
member_id: member.id,
custom_field_id: custom_field.id,
value: %{"_union_type" => "string", "_union_value" => value}
})
|> Ash.create(actor: system_actor)
end
defp log_in_user(conn, user) do
conn
|> Phoenix.ConnTest.init_test_session(%{})
|> AshAuthentication.Plug.Helpers.store_in_session(user)
end
end