defmodule MvWeb.MemberLive.IndexFieldPickerTest do @moduledoc """ §1.10 / §3.5 — the FilterDescriptor catalog behind the field picker. Fields are grouped Quick / Membership / Custom fields; a group with no available fields is omitted (renders no header). The catalog is derived purely from the field context and is serializable (the persistence basis for saved views, #549). """ use ExUnit.Case, async: true alias MvWeb.MemberLive.Index.FilterDescriptor defp group_keys(descriptors, group) do descriptors |> Enum.filter(&(&1.group == group)) |> Enum.map(& &1.key) end test "groups are ordered Quick / Membership / Custom fields" do context = %{ groups: [%{id: Ecto.UUID.generate(), name: "Board"}], fee_types: [%{id: Ecto.UUID.generate(), name: "Standard"}], custom_fields: [%{id: Ecto.UUID.generate(), name: "Newsletter", value_type: :boolean}] } descriptors = FilterDescriptor.all(context) assert Enum.map(FilterDescriptor.visible_groups(descriptors), &elem(&1, 0)) == [:quick, :membership, :custom_fields] end test "quick and membership fields are present with type-aware controls" do descriptors = FilterDescriptor.all(%{groups: [%{id: Ecto.UUID.generate(), name: "G"}]}) assert group_keys(descriptors, :quick) == [:payment, :active_former] assert :group in group_keys(descriptors, :membership) assert :join_date in group_keys(descriptors, :membership) assert :exit_date in group_keys(descriptors, :membership) payment = Enum.find(descriptors, &(&1.key == :payment)) assert payment.control == :payment_count end test "empty custom-fields group is omitted (no header)" do descriptors = FilterDescriptor.all(%{custom_fields: []}) groups = Enum.map(FilterDescriptor.visible_groups(descriptors), &elem(&1, 0)) refute :custom_fields in groups end test "membership omits Group/Fee type when the club has none" do descriptors = FilterDescriptor.all(%{groups: [], fee_types: []}) membership_keys = group_keys(descriptors, :membership) refute :group in membership_keys refute :fee_type in membership_keys # Join/exit date are always available. assert :join_date in membership_keys assert :exit_date in membership_keys end test "one custom-fields descriptor per filterable field; non-filterable types dropped" do date_id = Ecto.UUID.generate() bool_id = Ecto.UUID.generate() context = %{ custom_fields: [ %{id: date_id, name: "Birthday", value_type: :date}, %{id: bool_id, name: "Consent", value_type: :boolean}, %{id: Ecto.UUID.generate(), name: "Phone", value_type: :string} ] } custom = Enum.filter(FilterDescriptor.all(context), &(&1.group == :custom_fields)) assert Enum.map(custom, & &1.key) == [to_string(date_id), to_string(bool_id)] # Keys are plain strings — serializable for #549. assert Enum.all?(custom, &is_binary(&1.key)) assert Enum.find(custom, &(&1.key == to_string(date_id))).control == :date_range end end