Skip to content

Commit

Permalink
fix LiveViewTest stream limit handling to match js
Browse files Browse the repository at this point in the history
  • Loading branch information
SteffenDE committed Jan 21, 2024
1 parent 0c14b8a commit ce4b303
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 11 deletions.
44 changes: 33 additions & 11 deletions lib/phoenix_live_view/test/dom.ex
Original file line number Diff line number Diff line change
Expand Up @@ -430,20 +430,35 @@ defmodule Phoenix.LiveViewTest.DOM do
content_changed? && type == "stream" ->
children = updated_existing_children ++ updated_appended

# we always reduce over all streams, even if only one of them is actually relevant
# for this container...
new_children =
Enum.reduce(streams, children, fn item, acc ->
[ref, inserts, _deletes | _maybe_reset] = item
Enum.reduce(streams, updated_existing_children, fn item, acc ->
[ref, inserts, _deletes | maybe_reset] = item
reset = maybe_reset == [true]

# remove all children that are in a stream that was reset
acc =
if reset do
Enum.reject(acc, fn child ->
item_ref = attribute(child, "data-phx-stream")
item_ref == ref
end)
else
acc
end

Enum.reduce(inserts, acc, fn [id, insert_at, _limit], acc ->
old_index = Enum.find_index(acc, &(attribute(&1, "id") == id))
Enum.reduce(inserts, acc, fn [id, insert_at, limit], acc ->
old_index = Enum.find_index(children, &(attribute(&1, "id") == id))
current_index = Enum.find_index(acc, &(attribute(&1, "id") == id))

appended? = Enum.any?(updated_appended, &(attribute(&1, "id") == id))

existing? = Enum.any?(updated_existing_children, &(attribute(&1, "id") == id))
deleted? = MapSet.member?(stream_deletes, id)

child =
case old_index && Enum.at(acc, old_index) do
case old_index && Enum.at(children, old_index) do
nil -> nil
child -> set_attr(child, "data-phx-stream", ref)
end
Expand All @@ -459,19 +474,20 @@ defmodule Phoenix.LiveViewTest.DOM do
acc

# do not append existing child if already present, only update in place
old_index && insert_at == -1 && (existing? or appended?) ->
current_index && insert_at == -1 && (existing? or appended?) ->
if deleted? do
acc |> List.delete_at(old_index) |> List.insert_at(insert_at, child)
acc |> List.delete_at(current_index) |> List.insert_at(insert_at, child)
else
List.replace_at(acc, old_index, child)
List.replace_at(acc, current_index, child)
end

old_index && insert_at ->
acc |> List.delete_at(old_index) |> List.insert_at(insert_at, child)
current_index && insert_at ->
acc |> List.delete_at(current_index) |> List.insert_at(insert_at, child)

!old_index && insert_at ->
!current_index && insert_at ->
List.insert_at(acc, insert_at, child)
end
|> maybe_apply_stream_limit(limit)
end)
end)
|> Enum.reject(fn child ->
Expand Down Expand Up @@ -615,4 +631,10 @@ defmodule Phoenix.LiveViewTest.DOM do
do: Enum.map(values, &normalize_attribute_order/1)

defp normalize_attribute_order(value), do: value

defp maybe_apply_stream_limit(children, nil), do: children

defp maybe_apply_stream_limit(children, limit) do
Enum.take(children, limit)
end
end
178 changes: 178 additions & 0 deletions test/phoenix_live_view/integrations/stream_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,184 @@ defmodule Phoenix.LiveView.StreamTest do
end) =~ ~r/streams can only be consumed directly by a for comprehension/
end

describe "limit" do
test "limit is enforced on mount, but not dead render", %{conn: conn} do
conn = get(conn, "/stream/limit")
assert html_response(conn, 200) |> ids_in_ul_list() == [
"items-1",
"items-2",
"items-3",
"items-4",
"items-5",
"items-6",
"items-7",
"items-8",
"items-9",
"items-10"
]

{:ok, _lv, html} = live(conn)

assert ids_in_ul_list(html) == [
"items-6",
"items-7",
"items-8",
"items-9",
"items-10"
]
end

test "removes item at front when appending and limit is negative", %{conn: conn} do
{:ok, lv, _html} = live(conn, "/stream/limit")

assert lv |> render_hook("configure", %{"at" => "-1", "limit" => "-5"}) |> ids_in_ul_list()== [
"items-6",
"items-7",
"items-8",
"items-9",
"items-10"
]

assert lv |> render_hook("insert_1") |> ids_in_ul_list()== [
"items-7",
"items-8",
"items-9",
"items-10",
"items-11"
]

assert lv |> render_hook("insert_10") |> ids_in_ul_list()== [
"items-17",
"items-18",
"items-19",
"items-20",
"items-21"
]
end

test "removes item at back when prepending and limit is positive", %{conn: conn} do
{:ok, lv, _html} = live(conn, "/stream/limit")

assert lv |> render_hook("configure", %{"at" => "0", "limit" => "5"}) |> ids_in_ul_list() == [
"items-10",
"items-9",
"items-8",
"items-7",
"items-6"
]

assert lv |> render_hook("insert_1") |> ids_in_ul_list()== [
"items-11",
"items-10",
"items-9",
"items-8",
"items-7"
]

assert lv |> render_hook("insert_10") |> ids_in_ul_list()== [
"items-21",
"items-20",
"items-19",
"items-18",
"items-17"
]
end

test "does nothing if appending and positive limit is reached", %{conn: conn} do
{:ok, lv, _html} = live(conn, "/stream/limit")

assert lv |> render_hook("configure", %{"at" => "-1", "limit" => "5"}) |> ids_in_ul_list() == [
"items-1",
"items-2",
"items-3",
"items-4",
"items-5"
]

# adding new items should do nothing, as the limit is reached
assert lv |> render_hook("insert_1") |> ids_in_ul_list()== [
"items-1",
"items-2",
"items-3",
"items-4",
"items-5"
]

assert lv |> render_hook("insert_10") |> ids_in_ul_list() == [
"items-1",
"items-2",
"items-3",
"items-4",
"items-5"
]
end

test "does nothing if prepending and negative limit is reached", %{conn: conn} do
{:ok, lv, _html} = live(conn, "/stream/limit")

assert lv |> render_hook("configure", %{"at" => "0", "limit" => "-5"}) |> ids_in_ul_list() == [
"items-5",
"items-4",
"items-3",
"items-2",
"items-1"
]

# adding new items should do nothing, as the limit is reached
assert lv |> render_hook("insert_1") |> ids_in_ul_list()== [
"items-5",
"items-4",
"items-3",
"items-2",
"items-1"
]

assert lv |> render_hook("insert_10") |> ids_in_ul_list() == [
"items-5",
"items-4",
"items-3",
"items-2",
"items-1"
]
end

test "arbitrary index", %{conn: conn} do
{:ok, lv, _html} = live(conn, "/stream/limit")

assert lv |> render_hook("configure", %{"at" => "1", "limit" => "5"}) |> ids_in_ul_list() == [
"items-1",
"items-10",
"items-9",
"items-8",
"items-7"
]

assert lv |> render_hook("insert_10") |> ids_in_ul_list()== [
"items-1",
"items-20",
"items-19",
"items-18",
"items-17"
]

assert lv |> render_hook("configure", %{"at" => "1", "limit" => "-5"}) |> ids_in_ul_list() == [
"items-10",
"items-5",
"items-4",
"items-3",
"items-2"
]

assert lv |> render_hook("insert_10") |> ids_in_ul_list() == [
"items-20",
"items-5",
"items-4",
"items-3",
"items-2"
]
end
end

defp assert_pruned_stream(lv) do
stream = StreamLive.run(lv, fn socket -> {:reply, socket.assigns.streams.users, socket} end)
assert stream.inserts == []
Expand Down
1 change: 1 addition & 0 deletions test/support/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ defmodule Phoenix.LiveViewTest.Router do
live "/stream", StreamLive
live "/stream/reset", StreamResetLive
live "/stream/reset-lc", StreamResetLCLive
live "/stream/limit", StreamLimitLive

# healthy
live "/healthy/:category", HealthyLive
Expand Down

0 comments on commit ce4b303

Please sign in to comment.