test: updated tests
This commit is contained in:
parent
b71df98ba2
commit
41e3a52482
6 changed files with 412 additions and 16 deletions
|
|
@ -18,14 +18,14 @@ defmodule MvWeb.Components.SearchBarComponentTest do
|
|||
html =
|
||||
view
|
||||
|> element("form[role=search]")
|
||||
|> render_change(%{"query" => "Friedrich"})
|
||||
|> render_submit(%{"query" => "Friedrich"})
|
||||
|
||||
refute html =~ "Greta"
|
||||
|
||||
html =
|
||||
view
|
||||
|> element("form[role=search]")
|
||||
|> render_change(%{"query" => "Greta"})
|
||||
|> render_submit(%{"query" => "Greta"})
|
||||
|
||||
refute html =~ "Friedrich"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,12 +1,301 @@
|
|||
defmodule MvWeb.Components.SortHeaderComponentTest do
|
||||
use MvWeb.ConnCase, async: true
|
||||
use Phoenix.Component
|
||||
import Phoenix.LiveViewTest
|
||||
|
||||
test "renders sort header with correct attributes", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members")
|
||||
describe "rendering" do
|
||||
test "renders with correct attributes", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members")
|
||||
|
||||
assert view |> element("[data-testid='first_name']")
|
||||
# Test that the component renders with correct attributes
|
||||
assert has_element?(view, "[data-testid='first_name']")
|
||||
assert has_element?(view, "button[phx-value-field='city']")
|
||||
assert has_element?(view, "button[phx-value-field='first_name']", "First name")
|
||||
end
|
||||
|
||||
test "renders all sortable headers", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members")
|
||||
|
||||
sortable_fields = [:first_name, :email, :street, :house_number, :postal_code, :city, :phone_number, :join_date]
|
||||
|
||||
for field <- sortable_fields do
|
||||
assert has_element?(view, "button[phx-value-field='#{field}']")
|
||||
end
|
||||
end
|
||||
|
||||
test "renders correct labels", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members")
|
||||
|
||||
# Test specific labels
|
||||
assert has_element?(view, "button[phx-value-field='first_name']", "First name")
|
||||
assert has_element?(view, "button[phx-value-field='email']", "Email")
|
||||
assert has_element?(view, "button[phx-value-field='city']", "City")
|
||||
end
|
||||
end
|
||||
|
||||
describe "sort icons" do
|
||||
test "shows neutral icon for specific field when not sorted", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, html} = live(conn, "/members")
|
||||
|
||||
# The neutral icon has the opcity class we can test for
|
||||
# Test that EMAIL field specifically shows neutral icon
|
||||
assert has_element?(view, "[data-testid='email'] .opacity-40")
|
||||
|
||||
# Test that CITY field specifically shows neutral icon
|
||||
assert has_element?(view, "[data-testid='city'] .opacity-40")
|
||||
end
|
||||
|
||||
test "shows ascending icon for specific field when sorted ascending", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, html} = live(conn, "/members?query=&sort_field=city&sort_order=asc")
|
||||
|
||||
# Test that FIRST_NAME field specifically shows ascending icon
|
||||
# Test CSS classes - no opacity for active state
|
||||
refute has_element?(view, "[data-testid='city'] .opacity-40")
|
||||
|
||||
# Test that OTHER fields still show neutral icons
|
||||
assert has_element?(view, "[data-testid='first_name'] .opacity-40")
|
||||
|
||||
# Test HTML content - should contain chevron-up AND chevron-up-down
|
||||
assert html =~ "hero-chevron-up"
|
||||
assert html =~ "hero-chevron-up-down"
|
||||
|
||||
# Count occurrences to ensure only one ascending icon
|
||||
up_count = html |> String.split("hero-chevron-up ") |> length() |> Kernel.-(1)
|
||||
assert up_count == 1 # Should be exactly one chevron-up icon
|
||||
end
|
||||
|
||||
test "shows descending icon for specific field when sorted descending", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, html} = live(conn, "/members?query=&sort_field=email&sort_order=desc")
|
||||
|
||||
# Count occurrences to ensure only one descending icon
|
||||
down_count = html |> String.split("hero-chevron-down ") |> length() |> Kernel.-(1)
|
||||
assert down_count == 1 # Should be exactly one chevron-down icon
|
||||
end
|
||||
|
||||
test "multiple fields can have different icon states", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members?query=&sort_field=city&sort_order=asc")
|
||||
|
||||
# CITY field should be active (ascending)
|
||||
refute has_element?(view, "[data-testid='city'] .opacity-40")
|
||||
|
||||
# All other fields should be neutral
|
||||
assert has_element?(view, "[data-testid='first_name'] .opacity-40")
|
||||
assert has_element?(view, "[data-testid='email'] .opacity-40")
|
||||
assert has_element?(view, "[data-testid='street'] .opacity-40")
|
||||
assert has_element?(view, "[data-testid='house_number'] .opacity-40")
|
||||
assert has_element?(view, "[data-testid='postal_code'] .opacity-40")
|
||||
assert has_element?(view, "[data-testid='phone_number'] .opacity-40")
|
||||
assert has_element?(view, "[data-testid='join_date'] .opacity-40")
|
||||
end
|
||||
|
||||
test "icon state changes correctly when clicking different fields", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members")
|
||||
|
||||
# Start: all fields neutral except first name as default
|
||||
assert has_element?(view, "[data-testid='city'] .opacity-40")
|
||||
refute has_element?(view, "[data-testid='first_name'] .opacity-40")
|
||||
assert has_element?(view, "[data-testid='email'] .opacity-40")
|
||||
|
||||
# Click city - should become active
|
||||
view
|
||||
|> element("button[phx-value-field='city']")
|
||||
|> render_click()
|
||||
|
||||
# city should be active, email should still be neutral
|
||||
refute has_element?(view, "[data-testid='city'] .opacity-40")
|
||||
assert has_element?(view, "[data-testid='email'] .opacity-40")
|
||||
|
||||
# Click email - should switch active field
|
||||
view
|
||||
|> element("button[phx-value-field='email']")
|
||||
|> render_click()
|
||||
|
||||
# email should be active, city should be neutral again
|
||||
refute has_element?(view, "[data-testid='email'] .opacity-40")
|
||||
assert has_element?(view, "[data-testid='city'] .opacity-40")
|
||||
end
|
||||
|
||||
test "specific field shows correct icon for each sort state", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
|
||||
# Test EMAIL field specifically
|
||||
{:ok, view, html_asc} = live(conn, "/members?sort_field=email&sort_order=asc")
|
||||
assert html_asc =~ "hero-chevron-up"
|
||||
refute has_element?(view, "[data-testid='email'] .opacity-40")
|
||||
|
||||
{:ok, view, html_desc} = live(conn, "/members?sort_field=email&sort_order=desc")
|
||||
assert html_desc =~ "hero-chevron-down"
|
||||
refute has_element?(view, "[data-testid='email'] .opacity-40")
|
||||
|
||||
{:ok, view, html_neutral} = live(conn, "/members")
|
||||
assert html_neutral =~ "hero-chevron-up-down"
|
||||
assert has_element?(view, "[data-testid='email'] .opacity-40")
|
||||
end
|
||||
|
||||
test "icon distribution is correct for all fields", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
|
||||
# Test neutral state - all fields except first name (default) should show neutral icons
|
||||
{:ok, _view, html_neutral} = live(conn, "/members")
|
||||
|
||||
# Count neutral icons (should be 7 - one for each field)
|
||||
neutral_count = html_neutral |> String.split("hero-chevron-up-down") |> length() |> Kernel.-(1)
|
||||
assert neutral_count == 7
|
||||
|
||||
# Count active icons (should be 1)
|
||||
up_count = html_neutral |> String.split("hero-chevron-up ") |> length() |> Kernel.-(1)
|
||||
down_count = html_neutral |> String.split("hero-chevron-down ") |> length() |> Kernel.-(1)
|
||||
assert up_count == 1
|
||||
assert down_count == 0
|
||||
|
||||
# Test ascending state - one field active, others neutral
|
||||
{:ok, _view, html_asc} = live(conn, "/members?sort_field=first_name&sort_order=asc")
|
||||
|
||||
# Should have exactly 1 ascending icon and 7 neutral icons
|
||||
up_count = html_asc |> String.split("hero-chevron-up ") |> length() |> Kernel.-(1)
|
||||
neutral_count = html_asc |> String.split("hero-chevron-up-down") |> length() |> Kernel.-(1)
|
||||
down_count = html_asc |> String.split("hero-chevron-down ") |> length() |> Kernel.-(1)
|
||||
|
||||
assert up_count == 1
|
||||
assert neutral_count == 7
|
||||
assert down_count == 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "accessibility" do
|
||||
test "sets aria-label correctly for unsorted state", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members")
|
||||
|
||||
# Check aria-label for unsorted state
|
||||
assert has_element?(view, "button[phx-value-field='city'][aria-label='Click to sort']")
|
||||
end
|
||||
|
||||
test "sets aria-label correctly for ascending sort", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members?sort_field=first_name&sort_order=asc")
|
||||
|
||||
# Check aria-label for ascending sort
|
||||
assert has_element?(view, "button[phx-value-field='first_name'][aria-label='ascending']")
|
||||
end
|
||||
|
||||
test "sets aria-label correctly for descending sort", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members?sort_field=first_name&sort_order=desc")
|
||||
|
||||
# Check aria-label for descending sort
|
||||
assert has_element?(view, "button[phx-value-field='first_name'][aria-label='descending']")
|
||||
end
|
||||
|
||||
test "includes tooltip with correct aria-label", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members?sort_field=first_name&sort_order=asc")
|
||||
|
||||
# Check that tooltip div exists with correct data-tip
|
||||
assert has_element?(view, "[data-testid='first_name']")
|
||||
assert has_element?(view, "button[phx-value-field='first_name'][aria-label='ascending']")
|
||||
end
|
||||
|
||||
test "aria-labels work for all sortable fields", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members?sort_field=email&sort_order=desc")
|
||||
|
||||
# Test aria-labels for different fields
|
||||
assert has_element?(view, "button[phx-value-field='email'][aria-label='descending']")
|
||||
assert has_element?(view, "button[phx-value-field='first_name'][aria-label='Click to sort']")
|
||||
assert has_element?(view, "button[phx-value-field='city'][aria-label='Click to sort']")
|
||||
end
|
||||
end
|
||||
|
||||
describe "component behavior" do
|
||||
test "clicking sends sort message to parent", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members")
|
||||
|
||||
# Click on the first_name sort header
|
||||
view
|
||||
|> element("button[phx-value-field='first_name']")
|
||||
|> render_click()
|
||||
|
||||
# The component should send a message to the parent LiveView
|
||||
# This is tested indirectly through the URL change in integration tests
|
||||
end
|
||||
|
||||
test "component handles different field types correctly", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members")
|
||||
|
||||
# Test that different field types render correctly
|
||||
assert has_element?(view, "button[phx-value-field='first_name']")
|
||||
assert has_element?(view, "button[phx-value-field='email']")
|
||||
assert has_element?(view, "button[phx-value-field='join_date']")
|
||||
end
|
||||
end
|
||||
|
||||
describe "edge cases" do
|
||||
test "handles invalid sort field gracefully", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, html} = live(conn, "/members?sort_field=invalid_field&sort_order=asc")
|
||||
|
||||
# Should not crash and should default sorting for first name
|
||||
assert html =~ "hero-chevron-up-down"
|
||||
refute has_element?(view, "[data-testid='first_name'] .opacity-40")
|
||||
end
|
||||
|
||||
test "handles invalid sort order gracefully", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, html} = live(conn, "/members?sort_field=first_name&sort_order=invalid")
|
||||
|
||||
# Should default to ascending
|
||||
assert html =~ "hero-chevron-up"
|
||||
refute has_element?(view, "[data-testid='first_name'] [aria-label='ascending']")
|
||||
end
|
||||
|
||||
test "handles empty sort parameters", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, html} = live(conn, "/members?sort_field=&sort_order=")
|
||||
|
||||
# Should show neutral icons
|
||||
assert html =~ "hero-chevron-up-down"
|
||||
assert has_element?(view, "[data-testid='city'] .opacity-40")
|
||||
end
|
||||
end
|
||||
|
||||
describe "icon state transitions" do
|
||||
test "icon changes when sorting state changes", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members")
|
||||
|
||||
# Start with neutral state
|
||||
assert has_element?(view, "[data-testid='city'] .opacity-40")
|
||||
|
||||
# Click to sort ascending
|
||||
view
|
||||
|> element("button[phx-value-field='city']")
|
||||
|> render_click()
|
||||
|
||||
# Should now be ascending (no opacity class)
|
||||
refute has_element?(view, "[data-testid='city'] .opacity-40")
|
||||
end
|
||||
|
||||
test "multiple fields can be tested for icon states", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, html} = live(conn, "/members?sort_field=email&sort_order=desc")
|
||||
|
||||
# Email should be active (descending)
|
||||
assert html =~ "hero-chevron-down"
|
||||
refute has_element?(view, "[data-testid='email'] .opacity-40")
|
||||
|
||||
# Other fields should be neutral
|
||||
assert has_element?(view, "[data-testid='first_name'] .opacity-40")
|
||||
assert has_element?(view, "[data-testid='city'] .opacity-40")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -74,39 +74,132 @@ defmodule MvWeb.MemberLive.IndexTest do
|
|||
assert has_element?(index_view, "#flash-group", "Member create successfully")
|
||||
end
|
||||
|
||||
describe "sorting interaction" do
|
||||
describe "sorting integration" do
|
||||
test "clicking a column header toggles sort order and updates the URL", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members")
|
||||
|
||||
# The component data test ids are built as "<field>"
|
||||
# The component data test ids are built with the name of the field
|
||||
# First click – should sort ASC
|
||||
view
|
||||
|> element("[data-testid='email']")
|
||||
|> render_click()
|
||||
|
||||
# The LiveView pushes a patch with the new query params
|
||||
assert_patch(view, "/members?sort_field=email&sort_order=asc")
|
||||
assert_patch(view, "/members?query=&sort_field=email&sort_order=asc")
|
||||
|
||||
# Second click – toggles to DESC
|
||||
view
|
||||
|> element("[data-testid='email']")
|
||||
|> render_click()
|
||||
|
||||
assert_patch(view, "/members?sort_field=email&sort_order=desc")
|
||||
assert_patch(view, "/members?query=&sort_field=email&sort_order=desc")
|
||||
end
|
||||
|
||||
test "clicking different column header resets order to ascending", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members?sort_field=email&sort_order=desc")
|
||||
|
||||
# Click on a different column
|
||||
view
|
||||
|> element("[data-testid='first_name']")
|
||||
|> render_click()
|
||||
|
||||
assert_patch(view, "/members?query=&sort_field=first_name&sort_order=asc")
|
||||
end
|
||||
|
||||
test "all sortable columns work correctly", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members")
|
||||
|
||||
# default ascending sorting with first name
|
||||
assert has_element?(view, "[data-testid='first_name'][aria-label='ascending']")
|
||||
|
||||
sortable_fields = [:email, :street, :house_number, :postal_code, :city, :phone_number, :join_date]
|
||||
|
||||
for field <- sortable_fields do
|
||||
view
|
||||
|> element("[data-testid='#{field}']")
|
||||
|> render_click()
|
||||
|
||||
assert_patch(view, "/members?query=&sort_field=#{field}&sort_order=asc")
|
||||
end
|
||||
end
|
||||
|
||||
test "sorting works with search query", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members?query=test")
|
||||
|
||||
view
|
||||
|> element("[data-testid='email']")
|
||||
|> render_click()
|
||||
|
||||
assert_patch(view, "/members?query=test&sort_field=email&sort_order=asc")
|
||||
end
|
||||
|
||||
test "sorting maintains search query when toggling order", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members?query=test&sort_field=email&sort_order=asc")
|
||||
|
||||
view
|
||||
|> element("[data-testid='email']")
|
||||
|> render_click()
|
||||
|
||||
assert_patch(view, "/members?query=test&sort_field=email&sort_order=desc")
|
||||
end
|
||||
end
|
||||
|
||||
describe "URL param handling" do
|
||||
test "handle_params reads sort query and applies it", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
url = "/members?sort_field=email&sort_order=desc"
|
||||
{:ok, view, _html} = live(conn, "/members?query=&sort_field=email&sort_order=desc")
|
||||
|
||||
conn = get(conn, url)
|
||||
# Check that the sort state is correctly applied
|
||||
assert has_element?(view, "[data-testid='email'][aria-label='descending']")
|
||||
end
|
||||
|
||||
# The LiveView must have parsed the params and stored them as atoms.
|
||||
assert conn.assigns.sort_field == :email
|
||||
assert conn.assigns.sort_order == :desc
|
||||
test "handle_params handles invalid sort field gracefully", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members?query=&sort_field=invalid_field&sort_order=asc")
|
||||
|
||||
# Should not crash and should show default first name order
|
||||
assert has_element?(view, "[data-testid='first_name'][aria-label='ascending']")
|
||||
end
|
||||
|
||||
test "handle_params preserves search query with sort params", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members?query=test&sort_field=email&sort_order=desc")
|
||||
|
||||
# Both search and sort should be preserved
|
||||
assert has_element?(view, "[data-testid='email'][aria-label='descending']")
|
||||
end
|
||||
end
|
||||
|
||||
describe "search and sort integration" do
|
||||
test "search maintains sort state", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members?query=&sort_field=email&sort_order=desc")
|
||||
|
||||
# Perform search
|
||||
view
|
||||
|> element("[data-testid='search-input']")
|
||||
|> render_change(%{value: "test"})
|
||||
|
||||
# Sort state should be maintained
|
||||
assert has_element?(view, "[data-testid='email'][aria-label='descending']")
|
||||
end
|
||||
|
||||
test "sort maintains search state", %{conn: conn} do
|
||||
conn = conn_with_oidc_user(conn)
|
||||
{:ok, view, _html} = live(conn, "/members?query=test&sort_field=email&sort_order=asc")
|
||||
|
||||
# Perform sort
|
||||
view
|
||||
|> element("[data-testid='email']")
|
||||
|> render_click()
|
||||
|
||||
# Search state should be maintained
|
||||
assert_patch(view, "/members?query=test&sort_field=email&sort_order=desc")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue