phoenixframework / phoenix_live_view

Rich, real-time user experiences with server-rendered HTML
https://hex.pm/packages/phoenix_live_view
MIT License
6.22k stars 931 forks source link

0.20.2 breaks the order of stream elements when using reset in LiveViewTest #2970

Closed SteffenDE closed 10 months ago

SteffenDE commented 10 months ago

Environment

Actual behavior

File to reproduce:

Application.put_env(:phoenix, Example.Endpoint,
  http: [ip: {127, 0, 0, 1}, port: 5001],
  server: true,
  live_view: [signing_salt: "aaaaaaaa"],
  secret_key_base: String.duplicate("a", 64)
)

Mix.install([
  {:plug_cowboy, "~> 2.5"},
  {:jason, "~> 1.0"},
  {:phoenix, "~> 1.7.10", override: true},
  {:phoenix_live_view, "0.20.2"},
  {:floki, ">= 0.30.0"}
])

ExUnit.start()

defmodule Example.ErrorView do
  def render(template, _), do: Phoenix.Controller.status_message_from_template(template)
end

defmodule Example.HomeLive do
  use Phoenix.LiveView, layout: {__MODULE__, :live}

  def mount(_params, _session, socket) do
    socket
    |> stream(:items, [
      %{id: "a", name: "A"},
      %{id: "b", name: "B"},
      %{id: "c", name: "C"},
      %{id: "d", name: "D"}
    ])
    |> then(&{:ok, &1})
  end

  def handle_event("reorder", _, socket) do
    {:noreply,
     stream(
       socket,
       :items,
       [
         %{id: "e", name: "E"},
         %{id: "a", name: "A"},
         %{id: "f", name: "F"},
         %{id: "g", name: "G"}
       ],
       reset: true
     )}
  end

  def render("live.html", assigns) do
    ~H"""
    <script src="https://cdn.jsdelivr.net/npm/phoenix@1.7.10/priv/static/phoenix.min.js"></script>
    <script src="https://cdn.jsdelivr.net/gh/phoenixframework/phoenix_live_view@0.20.2/priv/static/phoenix_live_view.js"></script>
    <script>
      let liveSocket = new window.LiveView.LiveSocket("/live", window.Phoenix.Socket, {
        hooks: {
          FakeHook: {
            mounted() {}
          }
        }
      })
      liveSocket.connect()
    </script>
    <style>
      * { font-size: 1.1em; }
    </style>
    <%= @inner_content %>
    """
  end

  def render(assigns) do
    ~H"""
    <ul phx-update="stream" id="thelist">
      <li id={id} :for={{id, item} <- @streams.items}>
        <%= item.name %>
      </li>
    </ul>

    <button phx-click="reorder">Reorder</button>
    """
  end
end

defmodule Example.Router do
  use Phoenix.Router
  import Phoenix.LiveView.Router

  pipeline :browser do
    plug(:accepts, ["html"])
  end

  scope "/", Example do
    pipe_through(:browser)

    live("/", HomeLive, :index)
  end
end

defmodule Example.Endpoint do
  use Phoenix.Endpoint, otp_app: :phoenix
  socket("/live", Phoenix.LiveView.Socket)
  plug(Example.Router)
end

defmodule Example.HomeLiveTest do
  use ExUnit.Case

  import Phoenix.ConnTest
  import Plug.Conn
  import Phoenix.LiveViewTest

  @endpoint Example.Endpoint

  test "works properly" do
    conn = Phoenix.ConnTest.build_conn()

    {:ok, live, html} = live(conn, "/")

    assert Floki.parse(html)
           |> Floki.find("ul")
           |> Floki.find("li")
           |> Enum.map(fn html ->
             html |> Floki.text() |> String.trim()
           end) == ["A", "B", "C", "D"]

    assert html =
             live
             |> element("button")
             |> render_click()

    assert Floki.parse(html)
           |> Floki.find("ul")
           |> Floki.find("li")
           |> Enum.map(fn html ->
             html |> Floki.text() |> String.trim()
           end) == ["E", "A", "F", "G"]
  end
end

{:ok, _} = Supervisor.start_link([Example.Endpoint], strategy: :one_for_one)
ExUnit.run()
Process.sleep(:infinity)

Note that we initialize a stream of items as [a, b, c, d] and later reset the stream to [e, a, f, g]. With LV 0.20.2 (and main) the stream is reset as [a, e, f, g] instead.

Expected behavior

The test should pass. This is interesting because now the test clients shows the same broken behaviour that I am trying to fix for the real client in https://github.com/phoenixframework/phoenix_live_view/pull/2969

SteffenDE commented 10 months ago

Introduced in 703543049d33ca2d25efb1f6ef7f527df4d33b84