mitgliederverwaltung/test/mv_web/member_live/index_pagination_test.exs

96 lines
3.1 KiB
Elixir

defmodule MvWeb.MemberLive.IndexPaginationTest do
@moduledoc """
§1.7 — Mount loads only one keyset page, not the whole table.
§1.8 — phx-viewport-bottom fetches and appends the next page; once the last
page is reached no further fetch is issued.
"""
use MvWeb.ConnCase, async: false
import Phoenix.LiveViewTest
alias Mv.Helpers.SystemActor
require Ash.Query
# Matches the :overview default_limit / @page_limit in the LiveView.
@page_limit 50
defp seed_members(n) do
actor = SystemActor.get_system_actor()
Enum.each(1..n, fn i ->
# Zero-padded names keep first_name-ascending order deterministic.
idx = String.pad_leading(Integer.to_string(i), 4, "0")
{:ok, _} =
Mv.Membership.create_member(
%{first_name: "Page#{idx}", last_name: "Member", email: "page#{idx}@example.com"},
actor: actor
)
end)
end
defp row_count(html) do
~r/<tr[^>]*id="row-/ |> Regex.scan(html) |> length()
end
test "mount loads exactly one page when more members exist", %{conn: conn} do
seed_members(@page_limit + 10)
conn = conn_with_oidc_user(conn)
{:ok, _view, html} = live(conn, ~p"/members")
assert row_count(html) == @page_limit
# More rows remain, so the infinite-scroll sentinel is armed.
assert html =~ ~s(id="members-load-more-sentinel")
end
test "the sentinel appends the next page and stops at the last page", %{conn: conn} do
seed_members(@page_limit + 10)
conn = conn_with_oidc_user(conn)
{:ok, view, html} = live(conn, ~p"/members")
assert row_count(html) == @page_limit
# Fetch the next page: the remaining rows are appended to the stream.
html_after = render_hook(view, "load_more", %{})
assert row_count(html_after) == @page_limit + 10
# Last page reached: the sentinel is disarmed and a further fetch is a no-op.
refute html_after =~ ~s(id="members-load-more-sentinel")
html_again = render_hook(view, "load_more", %{})
assert row_count(html_again) == @page_limit + 10
end
test "selecting a row from a later page re-renders it as checked", %{conn: conn} do
seed_members(@page_limit + 10)
conn = conn_with_oidc_user(conn)
{:ok, view, _html} = live(conn, ~p"/members")
# Load the second page so its rows are in the loaded window.
render_hook(view, "load_more", %{})
# The highest-index member sorts last and lives on the second page.
actor = SystemActor.get_system_actor()
last_member =
Mv.Membership.Member
|> Ash.Query.sort(first_name: :asc)
|> Ash.read!(actor: actor)
|> List.last()
html = render_click(view, "select_member", %{"id" => last_member.id})
# The re-streamed row for a later-page member reflects the selection.
assert html =~ ~s(id="row-#{last_member.id}")
assert has_element?(view, ~s(tr#row-#{last_member.id} input[type="checkbox"][checked]))
end
test "single page does not arm the infinite-scroll sentinel", %{conn: conn} do
seed_members(3)
conn = conn_with_oidc_user(conn)
{:ok, _view, html} = live(conn, ~p"/members")
assert row_count(html) == 3
refute html =~ ~s(phx-viewport-bottom="load_more")
end
end