452 lines
13 KiB
Elixir
452 lines
13 KiB
Elixir
defmodule MvWeb.MemberLive.IndexFieldVisibilityTest do
|
|
@moduledoc """
|
|
Integration tests for field visibility dropdown functionality.
|
|
|
|
Tests cover:
|
|
- Field selection dropdown rendering
|
|
- Toggling field visibility
|
|
- URL parameter persistence
|
|
- Select all / deselect all
|
|
- Integration with member list display
|
|
- Custom fields visibility
|
|
"""
|
|
use MvWeb.ConnCase, async: true
|
|
|
|
import Phoenix.LiveViewTest
|
|
require Ash.Query
|
|
|
|
alias Mv.Membership.{CustomField, CustomFieldValue, Member}
|
|
|
|
setup do
|
|
# Create test members
|
|
{:ok, member1} =
|
|
Member
|
|
|> Ash.Changeset.for_create(:create_member, %{
|
|
first_name: "Alice",
|
|
last_name: "Anderson",
|
|
email: "alice@example.com",
|
|
street: "Main St",
|
|
city: "Berlin"
|
|
})
|
|
|> Ash.create()
|
|
|
|
{:ok, member2} =
|
|
Member
|
|
|> Ash.Changeset.for_create(:create_member, %{
|
|
first_name: "Bob",
|
|
last_name: "Brown",
|
|
email: "bob@example.com",
|
|
street: "Second St",
|
|
city: "Hamburg"
|
|
})
|
|
|> Ash.create()
|
|
|
|
# Create custom field
|
|
{:ok, custom_field} =
|
|
CustomField
|
|
|> Ash.Changeset.for_create(:create, %{
|
|
name: "membership_number",
|
|
value_type: :string,
|
|
show_in_overview: true
|
|
})
|
|
|> Ash.create()
|
|
|
|
# Create custom field values
|
|
{:ok, _cfv1} =
|
|
CustomFieldValue
|
|
|> Ash.Changeset.for_create(:create, %{
|
|
member_id: member1.id,
|
|
custom_field_id: custom_field.id,
|
|
value: "M001"
|
|
})
|
|
|> Ash.create()
|
|
|
|
{:ok, _cfv2} =
|
|
CustomFieldValue
|
|
|> Ash.Changeset.for_create(:create, %{
|
|
member_id: member2.id,
|
|
custom_field_id: custom_field.id,
|
|
value: "M002"
|
|
})
|
|
|> Ash.create()
|
|
|
|
%{
|
|
member1: member1,
|
|
member2: member2,
|
|
custom_field: custom_field
|
|
}
|
|
end
|
|
|
|
describe "field visibility dropdown" do
|
|
test "renders dropdown button", %{conn: conn} do
|
|
conn = conn_with_oidc_user(conn)
|
|
{:ok, _view, html} = live(conn, "/members")
|
|
|
|
assert html =~ "Columns"
|
|
assert html =~ ~s(aria-controls="field-visibility-menu")
|
|
end
|
|
|
|
test "opens dropdown when button is clicked", %{conn: conn} do
|
|
conn = conn_with_oidc_user(conn)
|
|
{:ok, view, _html} = live(conn, "/members")
|
|
|
|
# Initially closed
|
|
refute has_element?(view, "ul#field-visibility-menu")
|
|
|
|
# Click button
|
|
view
|
|
|> element("button[aria-controls='field-visibility-menu']")
|
|
|> render_click()
|
|
|
|
# Should be open now
|
|
assert has_element?(view, "ul#field-visibility-menu")
|
|
end
|
|
|
|
test "displays all member fields in dropdown", %{conn: conn} do
|
|
conn = conn_with_oidc_user(conn)
|
|
{:ok, view, _html} = live(conn, "/members")
|
|
|
|
# Open dropdown
|
|
view
|
|
|> element("button[aria-controls='field-visibility-menu']")
|
|
|> render_click()
|
|
|
|
html = render(view)
|
|
|
|
# Check for member fields (formatted labels)
|
|
assert html =~ "First Name" or html =~ "first_name"
|
|
assert html =~ "Email" or html =~ "email"
|
|
assert html =~ "Street" or html =~ "street"
|
|
end
|
|
|
|
test "displays custom fields in dropdown", %{conn: conn, custom_field: custom_field} do
|
|
conn = conn_with_oidc_user(conn)
|
|
{:ok, view, _html} = live(conn, "/members")
|
|
|
|
# Open dropdown
|
|
view
|
|
|> element("button[aria-controls='field-visibility-menu']")
|
|
|> render_click()
|
|
|
|
html = render(view)
|
|
|
|
assert html =~ custom_field.name
|
|
end
|
|
end
|
|
|
|
describe "field visibility toggling" do
|
|
test "hiding a field removes it from display", %{conn: conn} do
|
|
conn = conn_with_oidc_user(conn)
|
|
{:ok, view, _html} = live(conn, "/members")
|
|
|
|
# Verify email is visible initially
|
|
html = render(view)
|
|
assert html =~ "alice@example.com"
|
|
|
|
# Open dropdown and hide email
|
|
view
|
|
|> element("button[aria-controls='field-visibility-menu']")
|
|
|> render_click()
|
|
|
|
view
|
|
|> element("button[phx-click='select_item'][phx-value-item='email']")
|
|
|> render_click()
|
|
|
|
# Wait for update
|
|
:timer.sleep(100)
|
|
|
|
# Email should no longer be visible
|
|
html = render(view)
|
|
refute html =~ "alice@example.com"
|
|
refute html =~ "bob@example.com"
|
|
end
|
|
|
|
test "hiding custom field removes it from display", %{conn: conn, custom_field: custom_field} do
|
|
conn = conn_with_oidc_user(conn)
|
|
{:ok, view, _html} = live(conn, "/members")
|
|
|
|
# Verify custom field is visible initially
|
|
html = render(view)
|
|
assert html =~ "M001" or html =~ custom_field.name
|
|
|
|
# Open dropdown and hide custom field
|
|
view
|
|
|> element("button[aria-controls='field-visibility-menu']")
|
|
|> render_click()
|
|
|
|
custom_field_id = custom_field.id
|
|
custom_field_string = "custom_field_#{custom_field_id}"
|
|
|
|
view
|
|
|> element("button[phx-click='select_item'][phx-value-item='#{custom_field_string}']")
|
|
|> render_click()
|
|
|
|
# Wait for update
|
|
:timer.sleep(100)
|
|
|
|
# Custom field should no longer be visible
|
|
html = render(view)
|
|
refute html =~ "M001"
|
|
refute html =~ "M002"
|
|
end
|
|
end
|
|
|
|
describe "select all / deselect all" do
|
|
test "select all makes all fields visible", %{conn: conn} do
|
|
conn = conn_with_oidc_user(conn)
|
|
|
|
# Start with some fields hidden
|
|
{:ok, view, _html} = live(conn, "/members?fields=first_name")
|
|
|
|
# Open dropdown
|
|
view
|
|
|> element("button[aria-controls='field-visibility-menu']")
|
|
|> render_click()
|
|
|
|
# Click select all
|
|
view
|
|
|> element("button[phx-click='select_all']")
|
|
|> render_click()
|
|
|
|
# Wait for update
|
|
:timer.sleep(100)
|
|
|
|
# All fields should be visible
|
|
html = render(view)
|
|
assert html =~ "alice@example.com"
|
|
assert html =~ "Main St"
|
|
assert html =~ "Berlin"
|
|
end
|
|
|
|
test "deselect all hides all fields except first_name", %{conn: conn} do
|
|
conn = conn_with_oidc_user(conn)
|
|
{:ok, view, _html} = live(conn, "/members")
|
|
|
|
# Open dropdown
|
|
view
|
|
|> element("button[aria-controls='field-visibility-menu']")
|
|
|> render_click()
|
|
|
|
# Click deselect all
|
|
view
|
|
|> element("button[phx-click='select_none']")
|
|
|> render_click()
|
|
|
|
# Wait for update
|
|
:timer.sleep(100)
|
|
|
|
# Only first_name should be visible (it's always shown)
|
|
html = render(view)
|
|
# Email and street should be hidden
|
|
refute html =~ "alice@example.com"
|
|
refute html =~ "Main St"
|
|
end
|
|
end
|
|
|
|
describe "URL parameter persistence" do
|
|
test "field selection is persisted in URL", %{conn: conn} do
|
|
conn = conn_with_oidc_user(conn)
|
|
{:ok, view, _html} = live(conn, "/members")
|
|
|
|
# Open dropdown and hide email
|
|
view
|
|
|> element("button[aria-controls='field-visibility-menu']")
|
|
|> render_click()
|
|
|
|
view
|
|
|> element("button[phx-click='select_item'][phx-value-item='email']")
|
|
|> render_click()
|
|
|
|
# Wait for URL update
|
|
:timer.sleep(100)
|
|
|
|
# Check that URL contains fields parameter
|
|
# Note: In LiveView tests, we check the rendered HTML for the updated state
|
|
# The actual URL update happens via push_patch
|
|
end
|
|
|
|
test "loading page with fields parameter applies selection", %{conn: conn} do
|
|
conn = conn_with_oidc_user(conn)
|
|
|
|
# Load with first_name and city explicitly set in URL
|
|
# Note: Other fields may still be visible due to global settings
|
|
{:ok, view, _html} = live(conn, "/members?fields=first_name,city")
|
|
|
|
html = render(view)
|
|
|
|
# first_name and city should be visible
|
|
assert html =~ "Alice"
|
|
assert html =~ "Berlin"
|
|
|
|
# Note: email and street may still be visible if global settings allow it
|
|
# This test verifies that the URL parameters work, not that they hide other fields
|
|
end
|
|
|
|
test "fields parameter works with custom fields", %{conn: conn, custom_field: custom_field} do
|
|
conn = conn_with_oidc_user(conn)
|
|
custom_field_id = custom_field.id
|
|
|
|
# Load with custom field visible
|
|
{:ok, view, _html} =
|
|
live(conn, "/members?fields=first_name,custom_field_#{custom_field_id}")
|
|
|
|
html = render(view)
|
|
|
|
# Custom field should be visible
|
|
assert html =~ "M001" or html =~ custom_field.name
|
|
end
|
|
end
|
|
|
|
describe "integration with global settings" do
|
|
test "respects global settings when no user selection", %{conn: conn} do
|
|
# This test would require setting up global settings
|
|
# For now, we verify that the system works with default settings
|
|
conn = conn_with_oidc_user(conn)
|
|
{:ok, _view, html} = live(conn, "/members")
|
|
|
|
# All fields should be visible by default
|
|
assert html =~ "alice@example.com"
|
|
assert html =~ "Main St"
|
|
end
|
|
|
|
test "user selection overrides global settings", %{conn: conn} do
|
|
# This would require setting up global settings first
|
|
# Then verifying that user selection takes precedence
|
|
conn = conn_with_oidc_user(conn)
|
|
{:ok, view, _html} = live(conn, "/members")
|
|
|
|
# Hide a field via dropdown
|
|
view
|
|
|> element("button[aria-controls='field-visibility-menu']")
|
|
|> render_click()
|
|
|
|
view
|
|
|> element("button[phx-click='select_item'][phx-value-item='email']")
|
|
|> render_click()
|
|
|
|
:timer.sleep(100)
|
|
|
|
html = render(view)
|
|
refute html =~ "alice@example.com"
|
|
end
|
|
end
|
|
|
|
describe "edge cases" do
|
|
test "handles empty fields parameter", %{conn: conn} do
|
|
conn = conn_with_oidc_user(conn)
|
|
{:ok, _view, html} = live(conn, "/members?fields=")
|
|
|
|
# Should fall back to global settings
|
|
assert html =~ "alice@example.com"
|
|
end
|
|
|
|
test "handles invalid field names in URL", %{conn: conn} do
|
|
conn = conn_with_oidc_user(conn)
|
|
{:ok, _view, html} = live(conn, "/members?fields=invalid_field,another_invalid")
|
|
|
|
# Should ignore invalid fields and use defaults
|
|
assert html =~ "alice@example.com"
|
|
end
|
|
|
|
test "handles custom field that doesn't exist", %{conn: conn} do
|
|
conn = conn_with_oidc_user(conn)
|
|
{:ok, _view, html} = live(conn, "/members?fields=first_name,custom_field_nonexistent")
|
|
|
|
# Should work without errors
|
|
assert html =~ "Alice"
|
|
end
|
|
|
|
test "handles rapid toggling", %{conn: conn} do
|
|
conn = conn_with_oidc_user(conn)
|
|
{:ok, view, _html} = live(conn, "/members")
|
|
|
|
# Open dropdown
|
|
view
|
|
|> element("button[aria-controls='field-visibility-menu']")
|
|
|> render_click()
|
|
|
|
# Rapidly toggle a field multiple times
|
|
for _ <- 1..5 do
|
|
view
|
|
|> element("button[phx-click='select_item'][phx-value-item='email']")
|
|
|> render_click()
|
|
|
|
:timer.sleep(50)
|
|
end
|
|
|
|
# Should still work correctly
|
|
html = render(view)
|
|
assert html =~ "Alice"
|
|
end
|
|
end
|
|
|
|
describe "accessibility" do
|
|
test "dropdown has proper ARIA attributes", %{conn: conn} do
|
|
conn = conn_with_oidc_user(conn)
|
|
{:ok, _view, html} = live(conn, "/members")
|
|
|
|
assert html =~ ~s(aria-controls="field-visibility-menu")
|
|
assert html =~ ~s(aria-haspopup="menu")
|
|
assert html =~ ~s(role="button")
|
|
end
|
|
|
|
test "menu items have proper ARIA attributes when open", %{conn: conn} do
|
|
conn = conn_with_oidc_user(conn)
|
|
{:ok, view, _html} = live(conn, "/members")
|
|
|
|
# Open dropdown
|
|
view
|
|
|> element("button[aria-controls='field-visibility-menu']")
|
|
|> render_click()
|
|
|
|
html = render(view)
|
|
|
|
assert html =~ ~s(role="menu")
|
|
assert html =~ ~s(role="menuitemcheckbox")
|
|
assert html =~ ~s(aria-checked)
|
|
end
|
|
|
|
test "keyboard navigation works", %{conn: conn} do
|
|
conn = conn_with_oidc_user(conn)
|
|
{:ok, view, _html} = live(conn, "/members")
|
|
|
|
# Open dropdown
|
|
view
|
|
|> element("button[aria-controls='field-visibility-menu']")
|
|
|> render_click()
|
|
|
|
# Check that elements are keyboard accessible
|
|
html = render(view)
|
|
assert html =~ ~s(tabindex="0")
|
|
# Check that keyboard events are supported
|
|
assert html =~ ~s(phx-keydown="select_item")
|
|
assert html =~ ~s(phx-key="Enter")
|
|
end
|
|
|
|
test "keyboard activation with Enter key works", %{conn: conn} do
|
|
conn = conn_with_oidc_user(conn)
|
|
{:ok, view, _html} = live(conn, "/members")
|
|
|
|
# Verify email is visible initially
|
|
html = render(view)
|
|
assert html =~ "alice@example.com"
|
|
|
|
# Open dropdown
|
|
view
|
|
|> element("button[aria-controls='field-visibility-menu']")
|
|
|> render_click()
|
|
|
|
# Simulate Enter key press on email field button
|
|
view
|
|
|> element("button[phx-click='select_item'][phx-value-item='email']")
|
|
|> render_keydown(%{key: "Enter"})
|
|
|
|
# Wait for update
|
|
:timer.sleep(100)
|
|
|
|
# Email should no longer be visible
|
|
html = render(view)
|
|
refute html =~ "alice@example.com"
|
|
end
|
|
end
|
|
end
|