defmodule MvWeb.GroupLive.FormTest do @moduledoc """ Tests for the group create/edit form. Tests cover: - Creating groups - Editing groups - Form validations - Edge cases - Security """ use MvWeb.ConnCase, async: false import Phoenix.LiveViewTest use Gettext, backend: MvWeb.Gettext alias Mv.Fixtures describe "create form" do test "form renders with empty fields", %{conn: conn} do {:ok, view, html} = live(conn, "/groups/new") assert html =~ gettext("Create Group") or html =~ "create" or html =~ "Gruppe erstellen" assert has_element?(view, "form") end test "creates group successfully with name and description", %{conn: conn} do {:ok, view, _html} = live(conn, "/groups/new") form_data = %{ "name" => "New Group", "description" => "Group description" } view |> form("#group-form", group: form_data) |> render_submit() # Should redirect to groups list or show page assert_redirect(view, "/groups") end test "creates group successfully with name only (description optional)", %{conn: conn} do {:ok, view, _html} = live(conn, "/groups/new") form_data = %{ "name" => "Group Without Description" } view |> form("#group-form", group: form_data) |> render_submit() assert_redirect(view, "/groups") end test "shows error when name is missing", %{conn: conn} do {:ok, view, _html} = live(conn, "/groups/new") form_data = %{ "description" => "Description without name" } html = view |> form("#group-form", group: form_data) |> render_submit() assert html =~ gettext("required") or html =~ "name" or html =~ "error" or html =~ "erforderlich" end test "shows error when name exceeds 100 characters", %{conn: conn} do {:ok, view, _html} = live(conn, "/groups/new") long_name = String.duplicate("a", 101) form_data = %{"name" => long_name} html = view |> form("#group-form", group: form_data) |> render_submit() assert html =~ "100" or html =~ "length" or html =~ "error" or html =~ "Länge" end test "shows error when description exceeds 500 characters", %{conn: conn} do {:ok, view, _html} = live(conn, "/groups/new") long_description = String.duplicate("a", 501) form_data = %{ "name" => "Test Group", "description" => long_description } html = view |> form("#group-form", group: form_data) |> render_submit() assert html =~ "500" or html =~ "length" or html =~ "error" or html =~ "Länge" end test "shows error when name already exists (case-insensitive)", %{conn: conn} do _existing_group = Fixtures.group_fixture(%{name: "Existing Group"}) {:ok, view, _html} = live(conn, "/groups/new") form_data = %{ "name" => "EXISTING GROUP" } html = view |> form("#group-form", group: form_data) |> render_submit() # Check for a validation error on the name field in a robust way assert html =~ "name" or html =~ gettext("has already been taken") end test "shows error when name generates empty slug", %{conn: conn} do {:ok, view, _html} = live(conn, "/groups/new") form_data = %{ "name" => "!!!" } html = view |> form("#group-form", group: form_data) |> render_submit() assert html =~ "error" or html =~ "invalid" or html =~ "Fehler" or html =~ "ungültig" end end describe "edit form" do test "form renders with existing group data", %{conn: conn} do group = Fixtures.group_fixture(%{name: "Original Name", description: "Original Description"}) {:ok, view, html} = live(conn, "/groups/#{group.slug}/edit") assert html =~ "Original Name" assert html =~ "Original Description" assert has_element?(view, "form") end test "updates group name successfully (slug remains unchanged)", %{conn: conn} do group = Fixtures.group_fixture(%{name: "Original Name"}) original_slug = group.slug {:ok, view, _html} = live(conn, "/groups/#{group.slug}/edit") form_data = %{ "name" => "Updated Name" } view |> form("#group-form", group: form_data) |> render_submit() # Verify slug didn't change by checking redirect URL or reloading assert_redirect(view, "/groups/#{original_slug}") end test "updates group description successfully", %{conn: conn} do group = Fixtures.group_fixture(%{description: "Old Description"}) {:ok, view, _html} = live(conn, "/groups/#{group.slug}/edit") form_data = %{ "description" => "New Description" } view |> form("#group-form", group: form_data) |> render_submit() assert_redirect(view, "/groups/#{group.slug}") end test "shows error when updating to duplicate name (case-insensitive)", %{conn: conn} do _group1 = Fixtures.group_fixture(%{name: "Group One"}) group2 = Fixtures.group_fixture(%{name: "Group Two"}) {:ok, view, _html} = live(conn, "/groups/#{group2.slug}/edit") form_data = %{ "name" => "GROUP ONE" } html = view |> form("#group-form", group: form_data) |> render_submit() assert html =~ "already" or html =~ "taken" or html =~ "exists" or html =~ "error" or html =~ "bereits" or html =~ "vergeben" end test "slug is not displayed in form (immutable)", %{conn: conn} do group = Fixtures.group_fixture(%{name: "Test Group"}) {:ok, _view, html} = live(conn, "/groups/#{group.slug}/edit") # Slug should not be in form (it's immutable) refute html =~ ~r/slug.*input/i or html =~ ~r/input.*slug/i end end describe "edge cases" do test "handles whitespace trimming correctly", %{conn: conn} do {:ok, view, _html} = live(conn, "/groups/new") form_data = %{ "name" => " Trimmed Group " } view |> form("#group-form", group: form_data) |> render_submit() # Name should be trimmed assert_redirect(view, "/groups") end test "handles umlauts in name correctly", %{conn: conn} do {:ok, view, _html} = live(conn, "/groups/new") form_data = %{ "name" => "Müller Gruppe" } view |> form("#group-form", group: form_data) |> render_submit() assert_redirect(view, "/groups") end end describe "security" do @tag role: :member test "non-admin users cannot access create form", %{conn: conn} do result = live(conn, "/groups/new") assert match?({:error, {:redirect, %{to: _}}}, result) || match?({:error, {:live_redirect, %{to: _}}}, result) end @tag role: :member test "non-admin users cannot access edit form", %{conn: conn} do group = Fixtures.group_fixture() result = live(conn, "/groups/#{group.slug}/edit") assert match?({:error, {:redirect, %{to: _}}}, result) || match?({:error, {:live_redirect, %{to: _}}}, result) end @tag role: :unauthenticated test "unauthenticated users are redirected to login", %{conn: conn} do result = live(conn, "/groups/new") assert match?({:error, {:redirect, %{to: "/sign-in"}}}, result) || match?({:error, {:live_redirect, %{to: "/sign-in"}}}, result) end test "user cannot edit group with non-existent slug", %{conn: conn} do non_existent_slug = "non-existent-group-slug" result = live(conn, "/groups/#{non_existent_slug}/edit") assert match?({:error, {:redirect, %{to: "/groups"}}}, result) || match?({:error, {:live_redirect, %{to: "/groups"}}}, result) end test "user cannot edit group with wrong slug (case mismatch)", %{conn: conn} do group = Fixtures.group_fixture(%{name: "Test Group"}) wrong_case_slug = String.upcase(group.slug) result = live(conn, "/groups/#{wrong_case_slug}/edit") assert match?({:error, {:redirect, %{to: "/groups"}}}, result) || match?({:error, {:live_redirect, %{to: "/groups"}}}, result) end end end