317 lines
10 KiB
Elixir
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
|