Open LostKobrakai opened 4 months ago
Well, there is "some way", but it's indeed not very nice:
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"},
# please test your issue using the latest version of LV from GitHub!
{:phoenix_live_view, github: "phoenixframework/phoenix_live_view", branch: "main", override: true},
{: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
if params["raise"] == "mount", do: raise "oops"
socket
|> then(&{:ok, &1})
end
def handle_event("boom", _params, _socket), do: raise "boom"
def render("live.html", assigns) do
~H"""
<script src="/assets/phoenix/phoenix.js"></script>
<script src="/assets/phoenix_live_view/phoenix_live_view.js"></script>
<script>
let liveSocket = new window.LiveView.LiveSocket("/live", window.Phoenix.Socket)
liveSocket.connect()
</script>
<style>
* { font-size: 1.1em; }
</style>
<%= @inner_content %>
"""
end
def render(assigns) do
~H"""
<p>The LiveView content goes here</p>
<button phx-click="boom"></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 Plug.Static, from: {:phoenix, "priv/static"}, at: "/assets/phoenix"
plug Plug.Static, from: {:phoenix_live_view, "priv/static"}, at: "/assets/phoenix_live_view"
plug(Example.Router)
end
defmodule Example.HomeLiveTest do
use ExUnit.Case
import Phoenix.ConnTest
import Phoenix.LiveViewTest
@endpoint Example.Endpoint
test "works properly" do
conn = Phoenix.ConnTest.build_conn()
catch_exit(live(conn, "/?raise=mount"))
end
test "raise on event" do
conn = Phoenix.ConnTest.build_conn()
{:ok, view, _html} = live(conn, "/")
Process.flag(:trap_exit, true)
catch_exit(view |> element("button") |> render_click())
assert_receive {:EXIT, _, _}
end
end
{:ok, _} = Supervisor.start_link([Example.Endpoint], strategy: :one_for_one)
ExUnit.run()
For errors during mount you can use catch_exit
and for errors during events you need to use both catch_exit
and trap exits for the test process itself. Maybe there's a better way? @josevalim
I wasn't aware of catch_exit
. That might actually be enough for what I was expecting. I've always manually done monitoring, but that doesn't work well with LV as there's multiple linked processes involved.
Hmm, this is close, but the pid of the LV expectedly is a different one the view.pid
, so one cannot really match to a specific exit message.
Process.flag(:trap_exit, true)
assert {{exception, _}, _} =
catch_exit(
view
|> FloorplanPage.switch_rocker_configuration(switch_id)
|> FloorplanPage.add_trigger_function(target: "123", at: -1, mode: "toggle")
)
assert "Unknown channel" = Exception.message(exception)
This seems to works, but also leaves a big red error being logged.
You will have to @tag :capture_log
, as you do when testing processes. But generally speaking I think this is the direction to go.
Environment
Actual behavior
An exception in the LV crashes the test.
Expected behavior
Some way to catch the exception and at best assert on it.