diff --git a/test/membership/member_test.exs b/test/membership/member_test.exs index 705ab61..ab67a32 100644 --- a/test/membership/member_test.exs +++ b/test/membership/member_test.exs @@ -92,6 +92,67 @@ defmodule Mv.Membership.MemberTest do end end + describe "Settings-driven required fields" do + @valid_attrs %{ + first_name: "John", + last_name: "Doe", + email: "john@example.com" + } + + test "when first_name is required in settings, create without first_name fails", %{ + actor: actor + } do + {:ok, settings} = Membership.get_settings() + + {:ok, _} = + Membership.update_single_member_field(settings, + field: "first_name", + show_in_overview: true, + required: true + ) + + attrs = Map.delete(@valid_attrs, :first_name) + + assert {:error, %Ash.Error.Invalid{errors: errors}} = + Membership.create_member(attrs, actor: actor) + + assert error_message(errors, :first_name) =~ "can't be blank" + + # Reset so other tests (e.g. "First name is optional") are not affected + {:ok, settings} = Membership.get_settings() + + Membership.update_single_member_field(settings, + field: "first_name", + show_in_overview: true, + required: false + ) + end + + test "when first_name is required in settings, create with first_name succeeds", %{ + actor: actor + } do + {:ok, settings} = Membership.get_settings() + + {:ok, _} = + Membership.update_single_member_field(settings, + field: "first_name", + show_in_overview: true, + required: true + ) + + assert {:ok, _member} = Membership.create_member(@valid_attrs, actor: actor) + + # Reset + {:ok, settings} = Membership.get_settings() + + Membership.update_single_member_field(settings, + field: "first_name", + show_in_overview: true, + required: false + ) + end + end + describe "Authorization" do @valid_attrs %{ first_name: "John", diff --git a/test/membership/setting_test.exs b/test/membership/setting_test.exs index 531ab88..53ba492 100644 --- a/test/membership/setting_test.exs +++ b/test/membership/setting_test.exs @@ -39,6 +39,65 @@ defmodule Mv.Membership.SettingTest do assert error_message(errors, :club_name) =~ "must be present" end + + test "can update and read member_field_required" do + {:ok, settings} = Membership.get_settings() + + required_config = %{"first_name" => true, "last_name" => true} + + assert {:ok, updated} = + Membership.update_settings(settings, %{member_field_required: required_config}) + + assert updated.member_field_required["first_name"] == true + assert updated.member_field_required["last_name"] == true + end + + test "member_field_required rejects invalid keys" do + {:ok, settings} = Membership.get_settings() + + assert {:error, %Ash.Error.Invalid{errors: errors}} = + Membership.update_settings(settings, %{ + member_field_required: %{"invalid_field" => true} + }) + + assert error_message(errors, :member_field_required) =~ "Invalid member field" + end + + test "member_field_required rejects non-boolean values" do + {:ok, settings} = Membership.get_settings() + + assert {:error, %Ash.Error.Invalid{errors: errors}} = + Membership.update_settings(settings, %{ + member_field_required: %{"first_name" => "yes"} + }) + + assert error_message(errors, :member_field_required) =~ "must be booleans" + end + + test "update_single_member_field updates both visibility and required" do + {:ok, settings} = Membership.get_settings() + + assert {:ok, updated} = + Membership.update_single_member_field(settings, + field: "first_name", + show_in_overview: true, + required: true + ) + + assert updated.member_field_visibility["first_name"] == true + assert updated.member_field_required["first_name"] == true + + # Update same field to required: false + assert {:ok, updated2} = + Membership.update_single_member_field(updated, + field: "first_name", + show_in_overview: false, + required: false + ) + + assert updated2.member_field_visibility["first_name"] == false + assert updated2.member_field_required["first_name"] == false + end end # Helper function to extract error messages diff --git a/test/mv/vereinfacht/changes/sync_contact_test.exs b/test/mv/vereinfacht/changes/sync_contact_test.exs index aa102a5..f1dbc9c 100644 --- a/test/mv/vereinfacht/changes/sync_contact_test.exs +++ b/test/mv/vereinfacht/changes/sync_contact_test.exs @@ -54,7 +54,10 @@ defmodule Mv.Vereinfacht.Changes.SyncContactTest do attrs = %{ first_name: "API", last_name: "Test", - email: "api_test_#{System.unique_integer([:positive])}@example.com" + email: "api_test_#{System.unique_integer([:positive])}@example.com", + street: "Test St", + postal_code: "12345", + city: "Test City" } assert {:ok, member} = Membership.create_member(attrs, actor: system_actor) @@ -66,7 +69,14 @@ defmodule Mv.Vereinfacht.Changes.SyncContactTest do test "update_member succeeds and after_transaction runs without error (API may fail)" do set_vereinfacht_env() - member = Mv.Fixtures.member_fixture() + + member = + Mv.Fixtures.member_fixture(%{ + street: "Test St", + postal_code: "12345", + city: "Test City" + }) + system_actor = Mv.Helpers.SystemActor.get_system_actor() assert {:ok, updated} = diff --git a/test/mv_web/live/member_field_live/index_component_test.exs b/test/mv_web/live/member_field_live/index_component_test.exs index 037a77c..af8799f 100644 --- a/test/mv_web/live/member_field_live/index_component_test.exs +++ b/test/mv_web/live/member_field_live/index_component_test.exs @@ -5,8 +5,8 @@ defmodule MvWeb.MemberFieldLive.IndexComponentTest do Tests cover: - Rendering all member fields from Mv.Constants.member_fields() - Displaying show_in_overview status as badge (Yes/No) - - Displaying required status for required fields (first_name, last_name, email) - - Current status is displayed based on settings.member_field_visibility + - Displaying required status from settings.member_field_required (email is always required) + - Current status is displayed based on settings.member_field_visibility and member_field_required - Default status is "Yes" (visible) when not configured in settings """ use MvWeb.ConnCase, async: false @@ -45,11 +45,10 @@ defmodule MvWeb.MemberFieldLive.IndexComponentTest do assert html =~ "badge" or html =~ "Yes" or html =~ "No" end - test "displays required status for required fields", %{conn: conn} do + test "displays required status column", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/settings") - # Required fields: first_name, last_name, email - # Should have "Required" column or indicator + # Should have "Required" column; email is always required assert html =~ "Required" or html =~ "required" end @@ -85,40 +84,46 @@ defmodule MvWeb.MemberFieldLive.IndexComponentTest do end describe "required fields" do - test "marks first_name as required", %{conn: conn} do + test "marks email as required (always from settings)", %{conn: conn} do {:ok, _view, html} = live(conn, ~p"/settings") - # first_name should be marked as required - assert html =~ "first_name" or html =~ "First name" - # Should have required indicator - assert html =~ "required" or html =~ "Required" - end - - test "marks last_name as required", %{conn: conn} do - {:ok, _view, html} = live(conn, ~p"/settings") - - # last_name should be marked as required - assert html =~ "last_name" or html =~ "Last name" - # Should have required indicator - assert html =~ "required" or html =~ "Required" - end - - test "marks email as required", %{conn: conn} do - {:ok, _view, html} = live(conn, ~p"/settings") - - # email should be marked as required + # Email is always required assert html =~ "email" or html =~ "Email" - # Should have required indicator - assert html =~ "required" or html =~ "Required" + assert html =~ "Required" or html =~ "Optional" end - test "does not mark optional fields as required", %{conn: conn} do + test "when first_name is set required in settings, table shows Required", %{conn: conn} do + {:ok, settings} = Membership.get_settings() + + {:ok, _} = + Membership.update_single_member_field(settings, + field: "first_name", + show_in_overview: true, + required: true + ) + {:ok, _view, html} = live(conn, ~p"/settings") - # Optional fields should not have required indicator - # Check that street (optional) doesn't have required badge - # This test verifies that only required fields show the indicator - assert html =~ "street" or html =~ "Street" + # First name row should show Required (and Optional for others) + assert html =~ "First name" or html =~ "first_name" + assert html =~ "Required" + + # Reset + {:ok, settings} = Membership.get_settings() + + Membership.update_single_member_field(settings, + field: "first_name", + show_in_overview: true, + required: false + ) + end + + test "optional fields show Optional when not required in settings", %{conn: conn} do + {:ok, _view, html} = live(conn, ~p"/settings") + + # Email is required; other fields default to optional + assert html =~ "Optional" + assert html =~ "Required" end end end diff --git a/test/mv_web/member_live/form_error_handling_test.exs b/test/mv_web/member_live/form_error_handling_test.exs index 9e55cd8..44d7745 100644 --- a/test/mv_web/member_live/form_error_handling_test.exs +++ b/test/mv_web/member_live/form_error_handling_test.exs @@ -74,6 +74,45 @@ defmodule MvWeb.MemberLive.FormErrorHandlingTest do html =~ "Please correct" or html =~ "Bitte korrigieren" end + @tag :ui + test "shows validation error when settings-required field is missing", %{conn: conn} do + {:ok, settings} = Mv.Membership.get_settings() + + {:ok, _} = + Mv.Membership.update_single_member_field(settings, + field: "first_name", + show_in_overview: true, + required: true + ) + + conn = conn_with_oidc_user(conn) + {:ok, view, _html} = live(conn, "/members/new") + + # Submit without first_name (required in settings) + form_data = %{ + "member[last_name]" => "User", + "member[email]" => "newuser#{System.unique_integer([:positive])}@example.com" + } + + html = + view + |> form("#member-form", form_data) + |> render_submit() + + assert html =~ "error" or html =~ "Error" or html =~ "Fehler" or + html =~ "first_name" or html =~ "First name" or html =~ "can't be blank" or + html =~ "darf nicht leer sein" + + # Reset settings + {:ok, settings} = Mv.Membership.get_settings() + + Mv.Membership.update_single_member_field(settings, + field: "first_name", + show_in_overview: true, + required: false + ) + end + test "shows flash message when member update fails", %{conn: conn} do system_actor = Mv.Helpers.SystemActor.get_system_actor()