test: add tdd tests for backend state management of boolean custom filters
This commit is contained in:
parent
b84431879c
commit
dbec2d020f
1 changed files with 265 additions and 0 deletions
|
|
@ -656,4 +656,269 @@ defmodule MvWeb.MemberLive.IndexTest do
|
||||||
assert path =~ "show_current_cycle=true"
|
assert path =~ "show_current_cycle=true"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "boolean custom field filters" do
|
||||||
|
alias Mv.Membership.CustomField
|
||||||
|
|
||||||
|
# Helper to create a boolean custom field
|
||||||
|
defp create_boolean_custom_field(attrs \\ %{}) do
|
||||||
|
default_attrs = %{
|
||||||
|
name: "test_boolean_#{System.unique_integer([:positive])}",
|
||||||
|
value_type: :boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs = Map.merge(default_attrs, attrs)
|
||||||
|
|
||||||
|
CustomField
|
||||||
|
|> Ash.Changeset.for_create(:create, attrs)
|
||||||
|
|> Ash.create!()
|
||||||
|
end
|
||||||
|
|
||||||
|
# Helper to create a non-boolean custom field
|
||||||
|
defp create_string_custom_field(attrs \\ %{}) do
|
||||||
|
default_attrs = %{
|
||||||
|
name: "test_string_#{System.unique_integer([:positive])}",
|
||||||
|
value_type: :string
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs = Map.merge(default_attrs, attrs)
|
||||||
|
|
||||||
|
CustomField
|
||||||
|
|> Ash.Changeset.for_create(:create, attrs)
|
||||||
|
|> Ash.create!()
|
||||||
|
end
|
||||||
|
|
||||||
|
test "mount initializes boolean_custom_field_filters as empty map", %{conn: conn} do
|
||||||
|
conn = conn_with_oidc_user(conn)
|
||||||
|
{:ok, view, _html} = live(conn, "/members")
|
||||||
|
|
||||||
|
state = :sys.get_state(view.pid)
|
||||||
|
assert state.socket.assigns.boolean_custom_field_filters == %{}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "handle_params parses boolean_filter_<id> values correctly", %{conn: conn} do
|
||||||
|
conn = conn_with_oidc_user(conn)
|
||||||
|
boolean_field = create_boolean_custom_field()
|
||||||
|
|
||||||
|
# Test true value
|
||||||
|
{:ok, view1, _html} =
|
||||||
|
live(conn, "/members?boolean_filter_#{boolean_field.id}=true")
|
||||||
|
|
||||||
|
state1 = :sys.get_state(view1.pid)
|
||||||
|
filters1 = state1.socket.assigns.boolean_custom_field_filters
|
||||||
|
assert filters1[boolean_field.id] == :true
|
||||||
|
refute filters1[boolean_field.id] == "true"
|
||||||
|
|
||||||
|
# Test false value
|
||||||
|
{:ok, view2, _html} =
|
||||||
|
live(conn, "/members?boolean_filter_#{boolean_field.id}=false")
|
||||||
|
|
||||||
|
state2 = :sys.get_state(view2.pid)
|
||||||
|
filters2 = state2.socket.assigns.boolean_custom_field_filters
|
||||||
|
assert filters2[boolean_field.id] == :false
|
||||||
|
refute filters2[boolean_field.id] == "false"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "handle_params ignores non-existent custom field IDs", %{conn: conn} do
|
||||||
|
conn = conn_with_oidc_user(conn)
|
||||||
|
fake_id = Ecto.UUID.generate()
|
||||||
|
|
||||||
|
{:ok, view, _html} =
|
||||||
|
live(conn, "/members?boolean_filter_#{fake_id}=true")
|
||||||
|
|
||||||
|
state = :sys.get_state(view.pid)
|
||||||
|
filters = state.socket.assigns.boolean_custom_field_filters
|
||||||
|
|
||||||
|
# Filter should not be added for non-existent custom field
|
||||||
|
refute Map.has_key?(filters, fake_id)
|
||||||
|
assert filters == %{}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "handle_params ignores non-boolean custom fields", %{conn: conn} do
|
||||||
|
conn = conn_with_oidc_user(conn)
|
||||||
|
string_field = create_string_custom_field()
|
||||||
|
|
||||||
|
{:ok, view, _html} =
|
||||||
|
live(conn, "/members?boolean_filter_#{string_field.id}=true")
|
||||||
|
|
||||||
|
state = :sys.get_state(view.pid)
|
||||||
|
filters = state.socket.assigns.boolean_custom_field_filters
|
||||||
|
|
||||||
|
# Filter should not be added for non-boolean custom field
|
||||||
|
refute Map.has_key?(filters, string_field.id)
|
||||||
|
assert filters == %{}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "handle_params ignores invalid filter values", %{conn: conn} do
|
||||||
|
conn = conn_with_oidc_user(conn)
|
||||||
|
boolean_field = create_boolean_custom_field()
|
||||||
|
|
||||||
|
# Test various invalid values
|
||||||
|
invalid_values = ["1", "0", "yes", "no", "True", "False", "", "invalid", "null"]
|
||||||
|
|
||||||
|
for invalid_value <- invalid_values do
|
||||||
|
{:ok, view, _html} =
|
||||||
|
live(conn, "/members?boolean_filter_#{boolean_field.id}=#{invalid_value}")
|
||||||
|
|
||||||
|
state = :sys.get_state(view.pid)
|
||||||
|
filters = state.socket.assigns.boolean_custom_field_filters
|
||||||
|
|
||||||
|
# Invalid values should not be added to filters
|
||||||
|
refute Map.has_key?(filters, boolean_field.id),
|
||||||
|
"Invalid value '#{invalid_value}' should not be added to filters"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "handle_params handles multiple boolean filters simultaneously", %{conn: conn} do
|
||||||
|
conn = conn_with_oidc_user(conn)
|
||||||
|
boolean_field1 = create_boolean_custom_field()
|
||||||
|
boolean_field2 = create_boolean_custom_field()
|
||||||
|
|
||||||
|
{:ok, view, _html} =
|
||||||
|
live(
|
||||||
|
conn,
|
||||||
|
"/members?boolean_filter_#{boolean_field1.id}=true&boolean_filter_#{boolean_field2.id}=false"
|
||||||
|
)
|
||||||
|
|
||||||
|
state = :sys.get_state(view.pid)
|
||||||
|
filters = state.socket.assigns.boolean_custom_field_filters
|
||||||
|
|
||||||
|
assert filters[boolean_field1.id] == :true
|
||||||
|
assert filters[boolean_field2.id] == :false
|
||||||
|
assert map_size(filters) == 2
|
||||||
|
end
|
||||||
|
|
||||||
|
test "build_query_params includes active boolean filters and excludes nil filters", %{
|
||||||
|
conn: conn
|
||||||
|
} do
|
||||||
|
conn = conn_with_oidc_user(conn)
|
||||||
|
boolean_field1 = create_boolean_custom_field()
|
||||||
|
boolean_field2 = create_boolean_custom_field()
|
||||||
|
|
||||||
|
# Test with active filters
|
||||||
|
{:ok, view1, _html} =
|
||||||
|
live(
|
||||||
|
conn,
|
||||||
|
"/members?boolean_filter_#{boolean_field1.id}=true&boolean_filter_#{boolean_field2.id}=false"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Trigger a search to see if filters are preserved in URL
|
||||||
|
view1
|
||||||
|
|> element("[data-testid='search-input']")
|
||||||
|
|> render_change(%{value: "test"})
|
||||||
|
|
||||||
|
# Check that the patch includes boolean filters
|
||||||
|
path1 = assert_patch(view1)
|
||||||
|
assert path1 =~ "boolean_filter_#{boolean_field1.id}=true"
|
||||||
|
assert path1 =~ "boolean_filter_#{boolean_field2.id}=false"
|
||||||
|
|
||||||
|
# Test without filters (nil filters should not appear in URL)
|
||||||
|
{:ok, view2, _html} = live(conn, "/members")
|
||||||
|
|
||||||
|
# Trigger a search
|
||||||
|
view2
|
||||||
|
|> element("[data-testid='search-input']")
|
||||||
|
|> render_change(%{value: "test"})
|
||||||
|
|
||||||
|
# Check that no boolean_filter params are in URL
|
||||||
|
path2 = assert_patch(view2)
|
||||||
|
refute path2 =~ "boolean_filter_"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "boolean filters are preserved during navigation actions", %{conn: conn} do
|
||||||
|
conn = conn_with_oidc_user(conn)
|
||||||
|
boolean_field = create_boolean_custom_field()
|
||||||
|
|
||||||
|
{:ok, view, _html} =
|
||||||
|
live(conn, "/members?boolean_filter_#{boolean_field.id}=true")
|
||||||
|
|
||||||
|
# Test sort toggle preserves filter
|
||||||
|
view
|
||||||
|
|> element("[data-testid='email']")
|
||||||
|
|> render_click()
|
||||||
|
|
||||||
|
path1 = assert_patch(view)
|
||||||
|
assert path1 =~ "boolean_filter_#{boolean_field.id}=true"
|
||||||
|
|
||||||
|
# Test search change preserves filter
|
||||||
|
view
|
||||||
|
|> element("[data-testid='search-input']")
|
||||||
|
|> render_change(%{value: "test"})
|
||||||
|
|
||||||
|
path2 = assert_patch(view)
|
||||||
|
assert path2 =~ "boolean_filter_#{boolean_field.id}=true"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "boolean filters work together with cycle_status_filter", %{conn: conn} do
|
||||||
|
conn = conn_with_oidc_user(conn)
|
||||||
|
boolean_field = create_boolean_custom_field()
|
||||||
|
|
||||||
|
{:ok, view, _html} =
|
||||||
|
live(
|
||||||
|
conn,
|
||||||
|
"/members?cycle_status_filter=paid&boolean_filter_#{boolean_field.id}=true"
|
||||||
|
)
|
||||||
|
|
||||||
|
state = :sys.get_state(view.pid)
|
||||||
|
filters = state.socket.assigns.boolean_custom_field_filters
|
||||||
|
|
||||||
|
# Both filters should be set
|
||||||
|
assert filters[boolean_field.id] == :true
|
||||||
|
assert state.socket.assigns.cycle_status_filter == :paid
|
||||||
|
|
||||||
|
# Both should be in URL when triggering search
|
||||||
|
view
|
||||||
|
|> element("[data-testid='search-input']")
|
||||||
|
|> render_change(%{value: "test"})
|
||||||
|
|
||||||
|
path = assert_patch(view)
|
||||||
|
assert path =~ "cycle_status_filter=paid"
|
||||||
|
assert path =~ "boolean_filter_#{boolean_field.id}=true"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "handle_params removes filter when custom field is deleted", %{conn: conn} do
|
||||||
|
conn = conn_with_oidc_user(conn)
|
||||||
|
boolean_field = create_boolean_custom_field()
|
||||||
|
|
||||||
|
# Set up filter via URL
|
||||||
|
{:ok, view, _html} =
|
||||||
|
live(conn, "/members?boolean_filter_#{boolean_field.id}=true")
|
||||||
|
|
||||||
|
state_before = :sys.get_state(view.pid)
|
||||||
|
filters_before = state_before.socket.assigns.boolean_custom_field_filters
|
||||||
|
assert filters_before[boolean_field.id] == :true
|
||||||
|
|
||||||
|
# Delete the custom field
|
||||||
|
Ash.destroy!(boolean_field)
|
||||||
|
|
||||||
|
# Navigate again - filter should be removed since custom field no longer exists
|
||||||
|
{:ok, view2, _html} =
|
||||||
|
live(conn, "/members?boolean_filter_#{boolean_field.id}=true")
|
||||||
|
|
||||||
|
state_after = :sys.get_state(view2.pid)
|
||||||
|
filters_after = state_after.socket.assigns.boolean_custom_field_filters
|
||||||
|
|
||||||
|
# Filter should not be present for deleted custom field
|
||||||
|
refute Map.has_key?(filters_after, boolean_field.id)
|
||||||
|
assert filters_after == %{}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "handle_params handles URL-encoded custom field IDs correctly", %{conn: conn} do
|
||||||
|
conn = conn_with_oidc_user(conn)
|
||||||
|
boolean_field = create_boolean_custom_field()
|
||||||
|
|
||||||
|
# URL-encode the custom field ID (though UUIDs shouldn't need encoding normally)
|
||||||
|
encoded_id = URI.encode(boolean_field.id)
|
||||||
|
|
||||||
|
{:ok, view, _html} =
|
||||||
|
live(conn, "/members?boolean_filter_#{encoded_id}=true")
|
||||||
|
|
||||||
|
state = :sys.get_state(view.pid)
|
||||||
|
filters = state.socket.assigns.boolean_custom_field_filters
|
||||||
|
|
||||||
|
# Filter should work with URL-encoded ID
|
||||||
|
# Phoenix should decode it automatically, so we check with original ID
|
||||||
|
assert filters[boolean_field.id] == :true
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue