defmodule AiDebug.LiveViewLoggerTest do use ExUnit.Case, async: true alias AiDebug.LiveViewLogger # Test module that uses the logger defmodule LoggedLiveView do use Phoenix.LiveView use AiDebug.LiveViewLogger, log_level: :debug def mount(_params, _session, socket) do {:ok, assign(socket, :data, "test")} end def handle_event("test_event", params, socket) do {:noreply, assign(socket, :last_event, params)} end def handle_info(:test_message, socket) do {:noreply, socket} end def render(assigns) do ~H"""

Data: <%= @data %>

""" end end setup do # Enable logger for tests LiveViewLogger.enable() on_exit(fn -> LiveViewLogger.disable() end) :ok end describe "lifecycle logging" do test "logs mount events" do # Subscribe to logger events LiveViewLogger.subscribe(LoggedLiveView) # Create a test socket socket = %Phoenix.LiveView.Socket{ id: "test-socket", endpoint: AiDebugWeb.Endpoint, view: LoggedLiveView, assigns: %{__changed__: %{}} } # Mount the LiveView {:ok, _socket} = LoggedLiveView.mount(%{}, %{}, socket) # Should receive mount event assert_receive {:liveview_event, :mount, LoggedLiveView, metadata} assert metadata.socket_id == "test-socket" assert_receive {:liveview_event, :mount_completed, LoggedLiveView, metadata} assert is_integer(metadata.duration_us) end test "logs handle_event calls" do LiveViewLogger.subscribe(LoggedLiveView) socket = %Phoenix.LiveView.Socket{ id: "test-socket", endpoint: AiDebugWeb.Endpoint, view: LoggedLiveView, assigns: %{__changed__: %{}, data: "test"} } # Call handle_event {:noreply, _socket} = LoggedLiveView.handle_event("test_event", %{"value" => 42}, socket) assert_receive {:liveview_event, :handle_event, LoggedLiveView, metadata} assert metadata.event == "test_event" assert metadata.params == %{"value" => 42} assert_receive {:liveview_event, :handle_event_completed, LoggedLiveView, metadata} assert metadata.event == "test_event" assert metadata.reply? == false end test "logs handle_info calls" do LiveViewLogger.subscribe(LoggedLiveView) socket = %Phoenix.LiveView.Socket{ id: "test-socket", endpoint: AiDebugWeb.Endpoint, view: LoggedLiveView, assigns: %{__changed__: %{}, data: "test"} } # Call handle_info {:noreply, _socket} = LoggedLiveView.handle_info(:test_message, socket) assert_receive {:liveview_event, :handle_info, LoggedLiveView, metadata} assert metadata.message_type == :test_message assert_receive {:liveview_event, :handle_info_completed, LoggedLiveView, metadata} assert is_integer(metadata.duration_us) end end describe "statistics tracking" do test "tracks mount times" do # Clear any existing stats :persistent_term.put({LiveViewLogger, :stats, LoggedLiveView}, nil) socket = %Phoenix.LiveView.Socket{ id: "test-socket", endpoint: AiDebugWeb.Endpoint, view: LoggedLiveView, assigns: %{__changed__: %{}} } # Mount multiple times to generate stats for _ <- 1..5 do {:ok, _} = LoggedLiveView.mount(%{}, %{}, socket) end # Wait for async updates Process.sleep(100) stats = LiveViewLogger.get_stats(LoggedLiveView) assert stats.events >= 10 # At least mount + mount_completed for each assert stats.errors == 0 assert stats.avg_mount_time > 0 end test "tracks event handling times" do socket = %Phoenix.LiveView.Socket{ id: "test-socket", endpoint: AiDebugWeb.Endpoint, view: LoggedLiveView, assigns: %{__changed__: %{}, data: "test"} } # Handle multiple events for i <- 1..5 do {:noreply, _} = LoggedLiveView.handle_event("test_event", %{"n" => i}, socket) end Process.sleep(100) stats = LiveViewLogger.get_stats(LoggedLiveView) assert stats.avg_event_time > 0 end end describe "global enable/disable" do test "can be disabled globally" do LiveViewLogger.disable() assert LiveViewLogger.enabled?() == false # Should not log when disabled LiveViewLogger.subscribe_all() socket = %Phoenix.LiveView.Socket{ id: "test-socket", endpoint: AiDebugWeb.Endpoint, view: LoggedLiveView, assigns: %{__changed__: %{}} } {:ok, _} = LoggedLiveView.mount(%{}, %{}, socket) # Should not receive any events refute_receive {:liveview_event, _, _, _}, 100 end end describe "subscription" do test "can subscribe to specific module" do # Another test module defmodule OtherLiveView do use Phoenix.LiveView use AiDebug.LiveViewLogger def mount(_, _, socket), do: {:ok, socket} def render(assigns), do: ~H"
Other
" end # Subscribe only to LoggedLiveView LiveViewLogger.subscribe(LoggedLiveView) socket1 = %Phoenix.LiveView.Socket{ id: "test-1", endpoint: AiDebugWeb.Endpoint, view: LoggedLiveView, assigns: %{__changed__: %{}} } socket2 = %Phoenix.LiveView.Socket{ id: "test-2", endpoint: AiDebugWeb.Endpoint, view: OtherLiveView, assigns: %{__changed__: %{}} } {:ok, _} = LoggedLiveView.mount(%{}, %{}, socket1) {:ok, _} = OtherLiveView.mount(%{}, %{}, socket2) # Should only receive events from LoggedLiveView assert_receive {:liveview_event, :mount, LoggedLiveView, _} refute_receive {:liveview_event, :mount, OtherLiveView, _}, 100 end test "can subscribe to all modules" do LiveViewLogger.subscribe_all() socket = %Phoenix.LiveView.Socket{ id: "test-socket", endpoint: AiDebugWeb.Endpoint, view: LoggedLiveView, assigns: %{__changed__: %{}} } {:ok, _} = LoggedLiveView.mount(%{}, %{}, socket) # Should receive event with :all subscription assert_receive {:liveview_event, :mount, LoggedLiveView, _} end end end