diff --git a/lib/live_debugger_web.ex b/lib/live_debugger_web.ex index 5f4f2eb60..c709e6782 100644 --- a/lib/live_debugger_web.ex +++ b/lib/live_debugger_web.ex @@ -8,6 +8,7 @@ defmodule LiveDebuggerWeb do on_mount({LiveDebuggerWeb.Hooks.Flash, :add_hook}) on_mount({LiveDebuggerWeb.Hooks.IframeCheck, :add_hook}) + on_mount({LiveDebuggerWeb.Hooks.URL, :add_hook}) import Phoenix.HTML import LiveDebuggerWeb.Helpers diff --git a/lib/live_debugger_web/components/navbar.ex b/lib/live_debugger_web/components/navbar.ex index 0efb84665..864215679 100644 --- a/lib/live_debugger_web/components/navbar.ex +++ b/lib/live_debugger_web/components/navbar.ex @@ -61,17 +61,18 @@ defmodule LiveDebuggerWeb.Components.Navbar do def return_link(assigns) do ~H""" - <.link patch={@return_link} class={@class}> + <.link patch={@return_link} class={@class} id="return-button"> <.nav_icon icon="icon-arrow-left" /> """ end attr(:class, :any, default: nil, doc: "Additional classes to add to the link.") + attr(:return_to, :any, default: nil, doc: "Return to URL.") def settings_button(assigns) do ~H""" - <.link navigate={RoutesHelper.settings()} class={@class}> + <.link navigate={RoutesHelper.settings(@return_to)} class={@class} id="settings-button"> <.nav_icon icon="icon-settings" /> """ diff --git a/lib/live_debugger_web/components/tab_group.ex b/lib/live_debugger_web/components/tab_group.ex index 65847e41e..5cb8c2b32 100644 --- a/lib/live_debugger_web/components/tab_group.ex +++ b/lib/live_debugger_web/components/tab_group.ex @@ -9,20 +9,13 @@ defmodule LiveDebuggerWeb.Components.TabGroup do attr(:transport_pid, :any, required: true) attr(:grouped_lv_processes, :list, required: true) - attr(:window_link?, :boolean, default: true) def group(assigns) do ~H"""
-

- <%= if @window_link? do %> - <.link navigate={RoutesHelper.window_dashboard(@transport_pid)} class="window-link"> - <%= Parsers.pid_to_string(@transport_pid) %> - - <% else %> - <%= Parsers.pid_to_string(@transport_pid) %> - <% end %> +

+ <%= Parsers.pid_to_string(@transport_pid) %>

diff --git a/lib/live_debugger_web/helpers/routes_helper.ex b/lib/live_debugger_web/helpers/routes_helper.ex index 1f5b4757a..7083d9f35 100644 --- a/lib/live_debugger_web/helpers/routes_helper.ex +++ b/lib/live_debugger_web/helpers/routes_helper.ex @@ -28,7 +28,13 @@ defmodule LiveDebuggerWeb.Helpers.RoutesHelper do end @spec settings() :: String.t() - def settings() do + def settings(return_to \\ nil) + + def settings(nil) do ~p"/settings" end + + def settings(return_to) do + ~p"/settings?return_to=#{return_to}" + end end diff --git a/lib/live_debugger_web/hooks/iframe_check.ex b/lib/live_debugger_web/hooks/iframe_check.ex index e445729cc..f49e9311c 100644 --- a/lib/live_debugger_web/hooks/iframe_check.ex +++ b/lib/live_debugger_web/hooks/iframe_check.ex @@ -2,20 +2,47 @@ defmodule LiveDebuggerWeb.Hooks.IframeCheck do @moduledoc """ This hook is used to check if the current page is inside an iframe. It assigns the `:in_iframe?` assign based on the connect params. + + Mock is created for it to simulate LiveDebugger in iframe in e2e tests. """ - import Phoenix.LiveView - import Phoenix.Component - - def on_mount(:add_hook, _params, _session, socket) do - in_iframe? = - if connected?(socket) do - socket - |> get_connect_params() - |> Map.get("in_iframe?", false) - else - false - end - - {:cont, assign(socket, :in_iframe?, in_iframe?)} + + @callback on_mount( + :add_hook, + params :: map(), + session :: map(), + socket :: Phoenix.LiveView.Socket.t() + ) :: + {:cont, Phoenix.LiveView.Socket.t()} + + def on_mount(:add_hook, params, session, socket) do + impl().on_mount(:add_hook, params, session, socket) + end + + defp impl() do + Application.get_env( + :live_debugger, + :iframe_check, + __MODULE__.Impl + ) + end + + defmodule Impl do + @moduledoc false + + import Phoenix.LiveView + import Phoenix.Component + + def on_mount(:add_hook, _params, _session, socket) do + in_iframe? = + if connected?(socket) do + socket + |> get_connect_params() + |> Map.get("in_iframe?", false) + else + false + end + + {:cont, assign(socket, :in_iframe?, in_iframe?)} + end end end diff --git a/lib/live_debugger_web/hooks/url.ex b/lib/live_debugger_web/hooks/url.ex new file mode 100644 index 000000000..8533fcfba --- /dev/null +++ b/lib/live_debugger_web/hooks/url.ex @@ -0,0 +1,23 @@ +defmodule LiveDebuggerWeb.Hooks.URL do + @moduledoc """ + This hook assigns the `:url` assign based on the current URL. + It is triggered on handle_params callback. + """ + + import Phoenix.LiveView + import Phoenix.Component + + alias LiveDebugger.Utils.URL + + def on_mount(:add_hook, :not_mounted_at_router, _session, socket) do + {:cont, socket} + end + + def on_mount(:add_hook, _params, _session, socket) do + {:cont, attach_hook(socket, :url, :handle_params, &handle_params/3)} + end + + defp handle_params(_params, url, socket) do + {:cont, assign(socket, :url, URL.to_relative(url))} + end +end diff --git a/lib/live_debugger_web/live/channel_dashboard_live.ex b/lib/live_debugger_web/live/channel_dashboard_live.ex index c7147fd73..485c0f731 100644 --- a/lib/live_debugger_web/live/channel_dashboard_live.ex +++ b/lib/live_debugger_web/live/channel_dashboard_live.ex @@ -9,7 +9,6 @@ defmodule LiveDebuggerWeb.ChannelDashboardLive do alias LiveDebuggerWeb.Helpers.RoutesHelper alias LiveDebuggerWeb.Components.Navbar alias Phoenix.LiveView.JS - alias LiveDebugger.Utils.URL alias LiveDebugger.Structs.TreeNode @@ -21,10 +20,9 @@ defmodule LiveDebuggerWeb.ChannelDashboardLive do alias LiveDebuggerWeb.Components.NavigationMenu @impl true - def handle_params(params, url, socket) do + def handle_params(params, _url, socket) do socket |> assign_node_id(params) - |> assign(:url, URL.to_relative(url)) |> noreply() end @@ -55,7 +53,7 @@ defmodule LiveDebuggerWeb.ChannelDashboardLive do pid={Parsers.pid_to_string(@lv_process.result.pid)} />
- + <.nav_icon :if={@lv_process.ok?} diff --git a/lib/live_debugger_web/live/live_views_dashboard_live.ex b/lib/live_debugger_web/live/live_views_dashboard_live.ex index b6fc37215..5f59f560a 100644 --- a/lib/live_debugger_web/live/live_views_dashboard_live.ex +++ b/lib/live_debugger_web/live/live_views_dashboard_live.ex @@ -23,7 +23,7 @@ defmodule LiveDebuggerWeb.LiveViewsDashboardLive do
- +
diff --git a/lib/live_debugger_web/live/settings_live.ex b/lib/live_debugger_web/live/settings_live.ex index 092811a90..850eaa38d 100644 --- a/lib/live_debugger_web/live/settings_live.ex +++ b/lib/live_debugger_web/live/settings_live.ex @@ -8,12 +8,19 @@ defmodule LiveDebuggerWeb.SettingsLive do alias LiveDebuggerWeb.Components.Navbar alias LiveDebuggerWeb.Helpers.RoutesHelper + @impl true + def handle_params(params, _url, socket) do + socket + |> assign(:return_to, params["return_to"]) + |> noreply() + end + @impl true def render(assigns) do ~H"""
- +
diff --git a/lib/live_debugger_web/live/window_dashboard_live.ex b/lib/live_debugger_web/live/window_dashboard_live.ex index 4bf6895e8..cb3188440 100644 --- a/lib/live_debugger_web/live/window_dashboard_live.ex +++ b/lib/live_debugger_web/live/window_dashboard_live.ex @@ -1,4 +1,11 @@ defmodule LiveDebuggerWeb.WindowDashboardLive do + @moduledoc """ + This view is a variant of the LiveViews dashboard, but it is used to display LiveViews in the given window. + It cannot be accessed from the browser directly, but: + - it is used when there are many LiveViews in the same window, and we cannot find a single successor. + - in case of extension this replaces the LiveViews dashboard, since extension works in a single window. + """ + use LiveDebuggerWeb, :live_view alias LiveDebugger.Utils.Parsers @@ -35,7 +42,7 @@ defmodule LiveDebuggerWeb.WindowDashboardLive do /> - +
@@ -68,7 +75,6 @@ defmodule LiveDebuggerWeb.WindowDashboardLive do <% else %> diff --git a/test/live_debugger/channel_dashboard_test.exs b/test/live_debugger/channel_dashboard_test.exs index f1564e936..8e790f03a 100644 --- a/test/live_debugger/channel_dashboard_test.exs +++ b/test/live_debugger/channel_dashboard_test.exs @@ -109,7 +109,7 @@ defmodule LiveDebugger.ChannelDashboardTest do end @sessions 2 - feature "settings button exists and redirects to settings page", %{ + feature "settings button exists and redirects works as expected", %{ sessions: [dev_app, debugger] } do dev_app @@ -118,9 +118,48 @@ defmodule LiveDebugger.ChannelDashboardTest do debugger |> visit("/") |> click(first_link()) - |> assert_has(css("navbar a[href=\"/settings\"]")) - |> click(css("navbar a[href=\"/settings\"]")) + |> assert_has(css("div#traces", text: "Callback traces")) + |> assert_has(settings_button()) + |> click(settings_button()) |> assert_has(css("h1", text: "Settings")) + |> assert_has(return_button()) + |> click(return_button()) + |> assert_has(css("div#traces", text: "Callback traces")) + end + + @sessions 2 + feature "return button redirects to window dashboard in case of iframe", %{ + sessions: [dev_app, debugger] + } do + LiveDebugger.MockIframeCheck + |> stub(:on_mount, fn _, _, _, socket -> + {:cont, Phoenix.Component.assign(socket, :in_iframe?, true)} + end) + + dev_app + |> visit(@dev_app_url) + + debugger + |> visit("/") + |> click(first_link()) + |> assert_has(css("div#traces", text: "Callback traces")) + |> click(return_button()) + |> assert_has(css("h1", text: "Active LiveViews in a single window")) + end + + @sessions 2 + feature "return button redirects to active live views dashboard not in iframe", %{ + sessions: [dev_app, debugger] + } do + dev_app + |> visit(@dev_app_url) + + debugger + |> visit("/") + |> click(first_link()) + |> assert_has(css("div#traces", text: "Callback traces")) + |> click(return_button()) + |> assert_has(css("h1", text: "Active LiveViews")) end @sessions 2 @@ -371,4 +410,8 @@ defmodule LiveDebugger.ChannelDashboardTest do defp many_assigns_15_node_button() do css("#tree-node-button-15-component-tree-sidebar-content") end + + defp settings_button(), do: css("navbar a#settings-button") + + defp return_button(), do: css("navbar a#return-button") end diff --git a/test/live_debugger/live_views_dashboard_test.exs b/test/live_debugger/live_views_dashboard_test.exs index 847a05d99..7b20591a4 100644 --- a/test/live_debugger/live_views_dashboard_test.exs +++ b/test/live_debugger/live_views_dashboard_test.exs @@ -23,7 +23,7 @@ defmodule LiveDebugger.LiveViewsDashboardTest do end @sessions 2 - feature "settings button exists and redirects to settings page", %{ + feature "settings button exists and redirects works as expected", %{ sessions: [dev_app, debugger] } do dev_app @@ -31,9 +31,12 @@ defmodule LiveDebugger.LiveViewsDashboardTest do debugger |> visit("/") - |> assert_has(css("navbar a[href=\"/settings\"]")) - |> click(css("navbar a[href=\"/settings\"]")) + |> assert_has(settings_button()) + |> click(settings_button()) |> assert_has(css("h1", text: "Settings")) + |> assert_has(return_button()) + |> click(return_button()) + |> assert_has(title(text: "Active LiveViews")) end defp title(text: text), do: css("h1", text: text) @@ -41,4 +44,8 @@ defmodule LiveDebugger.LiveViewsDashboardTest do defp live_sessions(count: count), do: css("#live-sessions > div", count: count) defp refresh_button(), do: css("button[phx-click=\"refresh\"]") + + defp settings_button(), do: css("navbar a#settings-button") + + defp return_button(), do: css("navbar a#return-button") end diff --git a/test/live_debugger/window_dashboard_test.exs b/test/live_debugger/window_dashboard_test.exs index c91177e44..9bff71f29 100644 --- a/test/live_debugger/window_dashboard_test.exs +++ b/test/live_debugger/window_dashboard_test.exs @@ -2,7 +2,7 @@ defmodule LiveDebugger.WindowDashboardTest do use LiveDebugger.E2ECase @sessions 3 - feature "user can see only active live views in the given window", %{ + feature "user can see only active live views per window", %{ sessions: [dev_app1, dev_app2, debugger] } do dev_app1 @@ -13,8 +13,10 @@ defmodule LiveDebugger.WindowDashboardTest do |> assert_has(title(text: "Active LiveViews")) |> assert_has(live_sessions(count: 1)) + transport_pid = get_transport_pid(debugger) + debugger - |> click(window_link()) + |> visit("/transport_pid/#{transport_pid}") |> assert_has(title(text: "Active LiveViews in a single window")) |> assert_has(live_sessions(count: 1)) @@ -26,32 +28,65 @@ defmodule LiveDebugger.WindowDashboardTest do |> assert_has(live_sessions(count: 1)) debugger - |> visit("/") + |> click(return_button()) |> assert_has(title(text: "Active LiveViews")) |> assert_has(live_sessions(count: 1)) end @sessions 2 - feature "settings button exists and redirects to settings page", %{ + feature "settings button exists and redirects works as expected", %{ sessions: [dev_app, debugger] } do dev_app |> visit(@dev_app_url) + transport_pid = get_transport_pid(debugger) + debugger - |> visit("/") - |> click(window_link()) + |> visit("/transport_pid/#{transport_pid}") |> assert_has(title(text: "Active LiveViews in a single window")) - |> assert_has(css("navbar a[href=\"/settings\"]")) - |> click(css("navbar a[href=\"/settings\"]")) + |> assert_has(settings_button()) + |> click(settings_button()) |> assert_has(css("h1", text: "Settings")) + |> assert_has(return_button()) + |> click(return_button()) + |> assert_has(title(text: "Active LiveViews in a single window")) + end + + @sessions 2 + feature "return button does not exist in case of iframe", %{ + sessions: [dev_app, debugger] + } do + LiveDebugger.MockIframeCheck + |> stub(:on_mount, fn _, _, _, socket -> + {:cont, Phoenix.Component.assign(socket, :in_iframe?, true)} + end) + + dev_app + |> visit(@dev_app_url) + + transport_pid = get_transport_pid(debugger) + + debugger + |> visit("/transport_pid/#{transport_pid}") + |> assert_has(title(text: "Active LiveViews in a single window")) + |> refute_has(return_button()) end defp title(text: text), do: css("h1", text: text) defp live_sessions(count: count), do: css("#live-sessions ", count: count) - defp window_link(), do: css("#live-sessions a.window-link", count: 1) + defp get_transport_pid(debugger) do + debugger + |> visit("/") + |> find(css("#live-sessions .transport-pid")) + |> Element.text() + end defp refresh_button(), do: css("button[phx-click=\"refresh\"]") + + defp return_button(), do: css("navbar a#return-button") + + defp settings_button(), do: css("navbar a#settings-button") end diff --git a/test/support/e2e_case.ex b/test/support/e2e_case.ex index a47f2db3a..00040b36e 100644 --- a/test/support/e2e_case.ex +++ b/test/support/e2e_case.ex @@ -9,10 +9,22 @@ defmodule LiveDebugger.E2ECase do use Wallaby.Feature import Wallaby.Query + import Mox @moduletag :e2e @dev_app_url Application.compile_env(:live_debugger, :dev_app_url, "") + + setup :set_mox_from_context + + setup do + LiveDebugger.MockIframeCheck + |> stub(:on_mount, fn _, _, _, socket -> + {:cont, Phoenix.Component.assign(socket, :in_iframe?, false)} + end) + + :ok + end end end end diff --git a/test/test_helper.exs b/test/test_helper.exs index 5767f760b..e2a0d2aa2 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -5,6 +5,9 @@ if Application.get_env(:live_debugger, :e2e?, false) do Application.put_env(:wallaby, :base_url, LiveDebuggerWeb.Endpoint.url()) Application.put_env(:live_debugger, :dev_app_url, LiveDebuggerDev.Endpoint.url()) + + Mox.defmock(LiveDebugger.MockIframeCheck, for: LiveDebuggerWeb.Hooks.IframeCheck) + Application.put_env(:live_debugger, :iframe_check, LiveDebugger.MockIframeCheck) else Mox.defmock(LiveDebugger.MockModuleService, for: LiveDebugger.Services.System.ModuleService) Application.put_env(:live_debugger, :module_service, LiveDebugger.MockModuleService)