All checks were successful
deploy / deploy (push) Successful in 1m38s
URL-based offset pagination with ?page=N for bookmarkable pages. Admin views use push_patch, shop collection uses navigate links. Responsive on mobile with horizontal-scroll tables and stacking pagination controls. Includes dev seed script for testing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
156 lines
4.4 KiB
Elixir
156 lines
4.4 KiB
Elixir
defmodule Berrypod.PaginationTest do
|
|
use Berrypod.DataCase, async: true
|
|
|
|
alias Berrypod.Pagination
|
|
|
|
describe "page_numbers/1" do
|
|
test "returns all pages when total <= 7" do
|
|
assert Pagination.page_numbers(page(1, 1)) == [1]
|
|
assert Pagination.page_numbers(page(1, 5)) == [1, 2, 3, 4, 5]
|
|
assert Pagination.page_numbers(page(3, 7)) == [1, 2, 3, 4, 5, 6, 7]
|
|
end
|
|
|
|
test "shows ellipsis for large page counts" do
|
|
result = Pagination.page_numbers(page(1, 20))
|
|
assert result == [1, 2, :ellipsis, 20]
|
|
end
|
|
|
|
test "shows window around current page" do
|
|
result = Pagination.page_numbers(page(10, 20))
|
|
assert result == [1, :ellipsis, 9, 10, 11, :ellipsis, 20]
|
|
end
|
|
|
|
test "current page near start" do
|
|
result = Pagination.page_numbers(page(2, 20))
|
|
assert result == [1, 2, 3, :ellipsis, 20]
|
|
end
|
|
|
|
test "current page near end" do
|
|
result = Pagination.page_numbers(page(19, 20))
|
|
assert result == [1, :ellipsis, 18, 19, 20]
|
|
end
|
|
|
|
test "current page at last" do
|
|
result = Pagination.page_numbers(page(20, 20))
|
|
assert result == [1, :ellipsis, 19, 20]
|
|
end
|
|
|
|
test "8 pages with current at 4" do
|
|
result = Pagination.page_numbers(page(4, 8))
|
|
assert result == [1, :ellipsis, 3, 4, 5, :ellipsis, 8]
|
|
end
|
|
end
|
|
|
|
describe "showing_text/1" do
|
|
test "returns showing range" do
|
|
assert Pagination.showing_text(page(1, 5, 25, 120)) == "Showing 1\u201325 of 120"
|
|
assert Pagination.showing_text(page(2, 5, 25, 120)) == "Showing 26\u201350 of 120"
|
|
assert Pagination.showing_text(page(5, 5, 25, 120)) == "Showing 101\u2013120 of 120"
|
|
end
|
|
|
|
test "returns no results when empty" do
|
|
assert Pagination.showing_text(page(1, 1, 25, 0)) == "No results"
|
|
end
|
|
end
|
|
|
|
describe "parse_page/1" do
|
|
test "returns 1 for nil" do
|
|
assert Pagination.parse_page(%{}) == 1
|
|
end
|
|
|
|
test "parses valid string" do
|
|
assert Pagination.parse_page(%{"page" => "3"}) == 3
|
|
end
|
|
|
|
test "returns 1 for invalid string" do
|
|
assert Pagination.parse_page(%{"page" => "abc"}) == 1
|
|
assert Pagination.parse_page(%{"page" => "0"}) == 1
|
|
assert Pagination.parse_page(%{"page" => "-1"}) == 1
|
|
end
|
|
|
|
test "accepts integer values" do
|
|
assert Pagination.parse_page(%{"page" => 5}) == 5
|
|
end
|
|
|
|
test "uses custom key" do
|
|
assert Pagination.parse_page(%{"p" => "7"}, "p") == 7
|
|
end
|
|
end
|
|
|
|
describe "paginate/2" do
|
|
test "paginates a query" do
|
|
# Create some subscribers as test data
|
|
for i <- 1..7 do
|
|
%Berrypod.Newsletter.Subscriber{}
|
|
|> Berrypod.Newsletter.Subscriber.changeset(%{
|
|
email: "pag#{i}@example.com",
|
|
status: "confirmed",
|
|
confirmed_at: DateTime.utc_now() |> DateTime.truncate(:second),
|
|
consent_text: "test"
|
|
})
|
|
|> Berrypod.Repo.insert!()
|
|
end
|
|
|
|
import Ecto.Query
|
|
|
|
query =
|
|
from(s in Berrypod.Newsletter.Subscriber,
|
|
order_by: [asc: s.email]
|
|
)
|
|
|
|
# Page 1 of 3 (3 per page, 7 total)
|
|
result = Pagination.paginate(query, page: 1, per_page: 3)
|
|
assert length(result.items) == 3
|
|
assert result.page == 1
|
|
assert result.per_page == 3
|
|
assert result.total_count == 7
|
|
assert result.total_pages == 3
|
|
|
|
# Page 3 has 1 item
|
|
result = Pagination.paginate(query, page: 3, per_page: 3)
|
|
assert length(result.items) == 1
|
|
assert result.page == 3
|
|
|
|
# Page beyond range is clamped
|
|
result = Pagination.paginate(query, page: 99, per_page: 3)
|
|
assert result.page == 3
|
|
end
|
|
|
|
test "handles empty result set" do
|
|
import Ecto.Query
|
|
|
|
query =
|
|
from(s in Berrypod.Newsletter.Subscriber,
|
|
where: s.email == "nonexistent@nope.com"
|
|
)
|
|
|
|
result = Pagination.paginate(query, page: 1, per_page: 25)
|
|
assert result.items == []
|
|
assert result.total_count == 0
|
|
assert result.total_pages == 1
|
|
assert result.page == 1
|
|
end
|
|
end
|
|
|
|
# Helpers for building test structs
|
|
defp page(current, total) do
|
|
%Pagination{
|
|
items: [],
|
|
page: current,
|
|
per_page: 25,
|
|
total_count: total * 25,
|
|
total_pages: total
|
|
}
|
|
end
|
|
|
|
defp page(current, total_pages, per_page, total_count) do
|
|
%Pagination{
|
|
items: [],
|
|
page: current,
|
|
per_page: per_page,
|
|
total_count: total_count,
|
|
total_pages: total_pages
|
|
}
|
|
end
|
|
end
|